From d2c907541a177435b1f34b0b4a23e69eb1a75e5c Mon Sep 17 00:00:00 2001 From: elforjani13 <39470382+elforjani13@users.noreply.github.com> Date: Sat, 11 Jun 2022 00:38:00 +0200 Subject: [PATCH] feat: add project form dialog. --- src/components/DialogsContainer.js | 4 + .../containers/ProjectDialogContent.tsx | 22 +++++ .../containers/ProjectForm.schema.tsx | 13 +++ .../ProjectDialog/containers/ProjectForm.tsx | 71 +++++++++++++ .../containers/ProjectFormContent.tsx | 17 ++++ .../containers/ProjectFormFields.tsx | 99 +++++++++++++++++++ .../containers/ProjectFormFloatingActions.tsx | 48 +++++++++ .../containers/ProjectFormProvider.tsx | 33 +++++++ .../Dialogs/ProjectDialog/index.tsx | 51 ++++++++++ src/routes/dashboard.js | 7 ++ src/store/Project/projects.actions.ts | 14 +++ src/store/Project/projects.reducer.ts | 33 +++++++ src/store/Project/projects.selectors.ts | 24 +++++ src/store/Project/projects.type.ts | 4 + src/store/reducers.js | 2 + src/store/types.js | 2 + .../pages/Projects/ProjectFormDialog.scss | 20 ++++ 17 files changed, 464 insertions(+) create mode 100644 src/containers/Dialogs/ProjectDialog/containers/ProjectDialogContent.tsx create mode 100644 src/containers/Dialogs/ProjectDialog/containers/ProjectForm.schema.tsx create mode 100644 src/containers/Dialogs/ProjectDialog/containers/ProjectForm.tsx create mode 100644 src/containers/Dialogs/ProjectDialog/containers/ProjectFormContent.tsx create mode 100644 src/containers/Dialogs/ProjectDialog/containers/ProjectFormFields.tsx create mode 100644 src/containers/Dialogs/ProjectDialog/containers/ProjectFormFloatingActions.tsx create mode 100644 src/containers/Dialogs/ProjectDialog/containers/ProjectFormProvider.tsx create mode 100644 src/containers/Dialogs/ProjectDialog/index.tsx create mode 100644 src/store/Project/projects.actions.ts create mode 100644 src/store/Project/projects.reducer.ts create mode 100644 src/store/Project/projects.selectors.ts create mode 100644 src/store/Project/projects.type.ts create mode 100644 src/style/pages/Projects/ProjectFormDialog.scss diff --git a/src/components/DialogsContainer.js b/src/components/DialogsContainer.js index 8ddff3496..4cad1a95a 100644 --- a/src/components/DialogsContainer.js +++ b/src/components/DialogsContainer.js @@ -40,6 +40,8 @@ import BranchActivateDialog from '../containers/Dialogs/BranchActivateDialog'; import WarehouseActivateDialog from '../containers/Dialogs/WarehouseActivateDialog'; import CustomerOpeningBalanceDialog from '../containers/Dialogs/CustomerOpeningBalanceDialog'; import VendorOpeningBalanceDialog from '../containers/Dialogs/VendorOpeningBalanceDialog'; +import ProjectDialog from '../containers/Dialogs/ProjectDialog'; +import TaskDialog from '../containers/Dialogs/TaskDialog'; /** * Dialogs container. @@ -90,6 +92,8 @@ export default function DialogsContainer() { + + ); } diff --git a/src/containers/Dialogs/ProjectDialog/containers/ProjectDialogContent.tsx b/src/containers/Dialogs/ProjectDialog/containers/ProjectDialogContent.tsx new file mode 100644 index 000000000..e4aa08c56 --- /dev/null +++ b/src/containers/Dialogs/ProjectDialog/containers/ProjectDialogContent.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +import 'style/pages/Projects/ProjectFormDialog.scss'; + +import { ProjectFormProvider } from './ProjectFormProvider'; +import ProjectForm from './ProjectForm'; + +/** + * Project dialog content. + * @returns + */ +export default function ProjectDialogContent({ + // #ownProps + dialogName, + project, +}) { + return ( + + + + ); +} diff --git a/src/containers/Dialogs/ProjectDialog/containers/ProjectForm.schema.tsx b/src/containers/Dialogs/ProjectDialog/containers/ProjectForm.schema.tsx new file mode 100644 index 000000000..6738904f0 --- /dev/null +++ b/src/containers/Dialogs/ProjectDialog/containers/ProjectForm.schema.tsx @@ -0,0 +1,13 @@ +import * as Yup from 'yup'; +import intl from 'react-intl-universal'; +import { DATATYPES_LENGTH } from 'common/dataTypes'; + +const Schema = Yup.object().shape({ + contact: Yup.string(), + project_name: Yup.string(), + project_deadline: Yup.date(), + project_state: Yup.boolean(), + project_cost: Yup.number(), +}); + +export const CreateProjectFormSchema = Schema; diff --git a/src/containers/Dialogs/ProjectDialog/containers/ProjectForm.tsx b/src/containers/Dialogs/ProjectDialog/containers/ProjectForm.tsx new file mode 100644 index 000000000..cd7359898 --- /dev/null +++ b/src/containers/Dialogs/ProjectDialog/containers/ProjectForm.tsx @@ -0,0 +1,71 @@ +// @ts-nocheck +import React from 'react'; +import moment from 'moment'; +import intl from 'react-intl-universal'; + +import { Formik } from 'formik'; + +import { AppToaster } from 'components'; +import withDialogActions from 'containers/Dialog/withDialogActions'; + +import ProjectFormContent from './ProjectFormContent'; +import { CreateProjectFormSchema } from './ProjectForm.schema'; +import { useProjectFormContext } from './ProjectFormProvider'; + +import { compose } from 'utils'; + +const defaultInitialValues = { + contact:'', + project_name:'', + project_deadline: moment(new Date()).format('YYYY-MM-DD'), + project_state: false, + project_cost:'' +}; + +/** + * Project form + * @returns + */ +function ProjectForm({ + // #withDialogActions + closeDialog, +}) { + // project form dialog context. + const { dialogName } = useProjectFormContext(); + + // Initial form values + const initialValues = { + ...defaultInitialValues, + }; + + // Handles the form submit. + const handleFormSubmit = (values, { setSubmitting, setErrors }) => { + const form = {}; + + // Handle request response success. + const onSuccess = (response) => { + AppToaster.show({}); + closeDialog(dialogName); + }; + + // Handle request response errors. + const onError = ({ + response: { + data: { errors }, + }, + }) => { + setSubmitting(false); + }; + }; + + return ( + + ); +} + +export default compose(withDialogActions)(ProjectForm); diff --git a/src/containers/Dialogs/ProjectDialog/containers/ProjectFormContent.tsx b/src/containers/Dialogs/ProjectDialog/containers/ProjectFormContent.tsx new file mode 100644 index 000000000..0bcbed687 --- /dev/null +++ b/src/containers/Dialogs/ProjectDialog/containers/ProjectFormContent.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Form } from 'formik'; + +import ProjectFormFields from './ProjectFormFields'; +import ProjectFormFloatingActions from './ProjectFormFloatingActions'; + +/** + * Project form content. + */ +export default function ProjectFormContent() { + return ( +
+ + + + ); +} diff --git a/src/containers/Dialogs/ProjectDialog/containers/ProjectFormFields.tsx b/src/containers/Dialogs/ProjectDialog/containers/ProjectFormFields.tsx new file mode 100644 index 000000000..d01b1962c --- /dev/null +++ b/src/containers/Dialogs/ProjectDialog/containers/ProjectFormFields.tsx @@ -0,0 +1,99 @@ +import React from 'react'; +import intl from 'react-intl-universal'; + +import { Classes, Position, FormGroup, ControlGroup } from '@blueprintjs/core'; +import { FastField } from 'formik'; +import { DateInput } from '@blueprintjs/datetime'; +import { CLASSES } from 'common/classes'; +import classNames from 'classnames'; +import { + FFormGroup, + FInputGroup, + FCheckbox, + FMoneyInputGroup, + InputPrependText, + FormattedMessage as T, + CustomerSelectField, + FCustomerSelectField, +} from 'components'; +import { + inputIntent, + momentFormatter, + tansformDateValue, + handleDateChange, +} from 'utils'; + +/** + * Project form fields. + * @returns + */ +function ProjectFormFields() { + return ( +
+ {/*------------ Contact -----------*/} + + {({ form, field: { value }, meta: { error, touched } }) => ( + + { + form.setFieldValue('contact', customer.id); + }} + allowCreate={true} + popoverFill={true} + /> + + )} + + + {/*------------ Project Name -----------*/} + + + + {/*------------ DeadLine -----------*/} + + { + // })} + // value={tansformDateValue(value)} + popoverProps={{ + position: Position.BOTTOM, + minimal: true, + }} + /> + + + {/*------------ CheckBox -----------*/} + + + + {/*------------ Cost Estimate -----------*/} + + + + + + +
+ ); +} + +export default ProjectFormFields; diff --git a/src/containers/Dialogs/ProjectDialog/containers/ProjectFormFloatingActions.tsx b/src/containers/Dialogs/ProjectDialog/containers/ProjectFormFloatingActions.tsx new file mode 100644 index 000000000..1f118c72f --- /dev/null +++ b/src/containers/Dialogs/ProjectDialog/containers/ProjectFormFloatingActions.tsx @@ -0,0 +1,48 @@ +// @ts-nocheck +import React from 'react'; +import { useFormikContext } from 'formik'; +import { Intent, Button, Classes } from '@blueprintjs/core'; +import { FormattedMessage as T } from 'components'; +import { useProjectFormContext } from './ProjectFormProvider'; +import withDialogActions from 'containers/Dialog/withDialogActions'; +import { compose } from 'utils'; + +/** + * Project form floating actions. + * @returns + */ +function ProjectFormFloatingActions({ + // #withDialogActions + closeDialog, +}) { + // project form dialog context. + const { dialogName } = useProjectFormContext(); + + // Formik context. + const { isSubmitting } = useFormikContext(); + + // Handle close button click. + const handleCancelBtnClick = () => { + closeDialog(dialogName); + }; + + return ( +
+
+ + +
+
+ ); +} + +export default compose(withDialogActions)(ProjectFormFloatingActions); diff --git a/src/containers/Dialogs/ProjectDialog/containers/ProjectFormProvider.tsx b/src/containers/Dialogs/ProjectDialog/containers/ProjectFormProvider.tsx new file mode 100644 index 000000000..fc9561836 --- /dev/null +++ b/src/containers/Dialogs/ProjectDialog/containers/ProjectFormProvider.tsx @@ -0,0 +1,33 @@ +// @ts-nocheck +import React from 'react'; + +import { DialogContent } from 'components'; + +const ProjectFormContext = React.createContext(); + +/** + * Project form provider. + * @returns + */ + +function ProjectFormProvider({ + // #ownProps + dialogName, + projectId, + ...props +}) { + // State provider. + const provider = {}; + + return ( + + + + ); +} + +const useProjectFormContext = () => React.useContext(ProjectFormContext); + +export { ProjectFormProvider, useProjectFormContext }; diff --git a/src/containers/Dialogs/ProjectDialog/index.tsx b/src/containers/Dialogs/ProjectDialog/index.tsx new file mode 100644 index 000000000..c306c7402 --- /dev/null +++ b/src/containers/Dialogs/ProjectDialog/index.tsx @@ -0,0 +1,51 @@ +import React from 'react'; +import styled from 'styled-components'; +import { Dialog, DialogSuspense, FormattedMessage as T } from 'components'; +import withDialogRedux from 'components/DialogReduxConnect'; +import { compose } from 'utils'; + +const ProjectDialogContent = React.lazy( + () => import('./containers/ProjectDialogContent'), +); + +/** + * Project dialog. + * @returns + */ +function ProjectDialog({ dialogName, payload: { projectId = null }, isOpen }) { + return ( + + + + + + ); +} + +export default compose(withDialogRedux())(ProjectDialog); + +// const ProjectDialogRoot = styled(Dialog)` +// .bp3-dialog-body { +// .bp3-form-group { +// margin-bottom: 15px; +// margin-top: 15px; + +// label.bp3-label { +// margin-bottom: 3px; +// font-size: 13px; +// } +// } + +// .bp3-dialog-footer { +// padding-top: 10px; +// } +// } +// `; diff --git a/src/routes/dashboard.js b/src/routes/dashboard.js index 9283b492f..de351dfc6 100644 --- a/src/routes/dashboard.js +++ b/src/routes/dashboard.js @@ -969,6 +969,13 @@ export const getDashboardRoutes = () => [ ), pageTitle: intl.get('sidebar.transactions_locaking'), }, + { + path: '/projects', + component: lazy(() => + import('../containers/Projects/containers/ProjectsList'), + ), + pageTitle: 'Projects', + }, // Homepage { path: `/`, diff --git a/src/store/Project/projects.actions.ts b/src/store/Project/projects.actions.ts new file mode 100644 index 000000000..f39531d81 --- /dev/null +++ b/src/store/Project/projects.actions.ts @@ -0,0 +1,14 @@ +import t from 'store/types'; + +export const setProjectsTableState = (queries) => { + return { + type: t.PROJECTS_TABLE_STATE_SET, + payload: { queries }, + }; +}; + +export const resetProjectsTableState = () => { + return { + type: t.PROJECTS_TABLE_STATE_RESET, + }; +}; diff --git a/src/store/Project/projects.reducer.ts b/src/store/Project/projects.reducer.ts new file mode 100644 index 000000000..cd219035b --- /dev/null +++ b/src/store/Project/projects.reducer.ts @@ -0,0 +1,33 @@ +import { createReducer } from '@reduxjs/toolkit'; +import { persistReducer, purgeStoredState } from 'redux-persist'; +import storage from 'redux-persist/lib/storage'; +import { createTableStateReducers } from 'store/tableState.reducer'; +import t from 'store/types'; + +export const defaultTableQuery = { + pageSize: 20, + pageIndex: 0, + filterRoles: [], + viewSlug: null, +}; + +const initialState = { + tableState: defaultTableQuery, +}; + +const STORAGE_KEY = 'bigcapital:projects'; + +const CONFIG = { + key: STORAGE_KEY, + whitelist: [], + storage, +}; +const reducerInstance = createReducer(initialState, { + ...createTableStateReducers('PROJECTS', defaultTableQuery), + + [t.RESET]: () => { + purgeStoredState(CONFIG); + }, +}); + +export default persistReducer(CONFIG, reducerInstance); diff --git a/src/store/Project/projects.selectors.ts b/src/store/Project/projects.selectors.ts new file mode 100644 index 000000000..10ab19fab --- /dev/null +++ b/src/store/Project/projects.selectors.ts @@ -0,0 +1,24 @@ +import { isEqual } from 'lodash'; +import { createDeepEqualSelector } from 'utils'; +import { paginationLocationQuery } from 'store/selectors'; +import { defaultTableQuery } from './projects.reducer'; + +const projectsTableState = (state) => state.projects.tableState; + +// Retrieve projects table query. +export const getProjectsTableStateFactory = () => + createDeepEqualSelector( + paginationLocationQuery, + projectsTableState, + (locationQuery, tableState) => { + return { + ...locationQuery, + ...tableState, + }; + }, + ); + +export const isProjectsTableStateChangedFactory = () => + createDeepEqualSelector(projectsTableState, (tableState) => { + return !isEqual(tableState, defaultTableQuery); + }); diff --git a/src/store/Project/projects.type.ts b/src/store/Project/projects.type.ts new file mode 100644 index 000000000..8026b0cf6 --- /dev/null +++ b/src/store/Project/projects.type.ts @@ -0,0 +1,4 @@ +export default { + PROJECTS_TABLE_STATE_SET: 'PROJECTS/TABLE_STATE_SET', + PROJECTS_TABLE_STATE_RESET: 'PROJECTS/TABLE_STATE_RESET', +}; diff --git a/src/store/reducers.js b/src/store/reducers.js index 0223270ad..b89241911 100644 --- a/src/store/reducers.js +++ b/src/store/reducers.js @@ -35,6 +35,7 @@ import plans from './plans/plans.reducer'; import creditNotes from './CreditNote/creditNote.reducer'; import vendorCredit from './VendorCredit/VendorCredit.reducer'; import warehouseTransfers from './WarehouseTransfer/warehouseTransfer.reducer'; +import projects from './Project/projects.reducer'; const appReducer = combineReducers({ authentication, @@ -70,6 +71,7 @@ const appReducer = combineReducers({ creditNotes, vendorCredit, warehouseTransfers, + projects, }); // Reset the state of a redux store diff --git a/src/store/types.js b/src/store/types.js index 079344984..602953034 100644 --- a/src/store/types.js +++ b/src/store/types.js @@ -31,6 +31,7 @@ import inventoryAdjustments from './inventoryAdjustments/inventoryAdjustment.typ import creditNote from './CreditNote/creditNote.type'; import vendorCredit from './VendorCredit/vendorCredit.type'; import WarehouseTransfer from './WarehouseTransfer/warehouseTransfer.type'; +import projects from './Project/projects.type' import plans from './plans/plans.types'; export default { @@ -68,4 +69,5 @@ export default { ...creditNote, ...vendorCredit, ...WarehouseTransfer, + ...projects }; diff --git a/src/style/pages/Projects/ProjectFormDialog.scss b/src/style/pages/Projects/ProjectFormDialog.scss new file mode 100644 index 000000000..31801f7ee --- /dev/null +++ b/src/style/pages/Projects/ProjectFormDialog.scss @@ -0,0 +1,20 @@ +.dialog--project-form { + width: 650px; + + .bp3-dialog-body { + .bp3-form-group { + margin-bottom: 15px; + margin-top: 15px; + + label.bp3-label { + margin-bottom: 3px; + font-size: 13px; + } + } + + + } + .bp3-dialog-footer { + padding-top: 10px; + } +}