From 01e2c243877fe4f1d10e3ae370c6d5e5c9334453 Mon Sep 17 00:00:00 2001 From: elforjani13 <39470382+elforjani13@users.noreply.github.com> Date: Mon, 19 Sep 2022 22:37:11 +0200 Subject: [PATCH] feat: add project billable entries dialog. --- src/components/DialogsContainer.tsx | 3 +- src/constants/dialogs.ts | 1 + .../ProjectBillableEntriesForm.schema.tsx | 6 ++ .../ProjectBillableEntriesForm.tsx | 54 ++++++++++ .../ProjectBillableEntriesFormContent.tsx | 17 +++ ...rojectBillableEntriesFormDialogContent.tsx | 22 ++++ .../ProjectBillableEntriesFormFields.tsx | 48 +++++++++ ...jectBillableEntriesFormFloatingActions.tsx | 48 +++++++++ .../ProjectBillableEntriesFormProvider.tsx | 53 +++++++++ .../components.tsx | 102 ++++++++++++++++++ .../index.tsx | 58 ++++++++++ src/containers/Projects/hooks/index.ts | 3 +- .../Projects/hooks/projectBillableEntries.tsx | 27 +++++ src/containers/Projects/hooks/type.ts | 5 + 14 files changed, 445 insertions(+), 2 deletions(-) create mode 100644 src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesForm.schema.tsx create mode 100644 src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesForm.tsx create mode 100644 src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormContent.tsx create mode 100644 src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormDialogContent.tsx create mode 100644 src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormFields.tsx create mode 100644 src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormFloatingActions.tsx create mode 100644 src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormProvider.tsx create mode 100644 src/containers/Projects/containers/ProjectBillableEntriesFormDialog/components.tsx create mode 100644 src/containers/Projects/containers/ProjectBillableEntriesFormDialog/index.tsx create mode 100644 src/containers/Projects/hooks/projectBillableEntries.tsx diff --git a/src/components/DialogsContainer.tsx b/src/components/DialogsContainer.tsx index f4e1e58e5..7b6fa7cf1 100644 --- a/src/components/DialogsContainer.tsx +++ b/src/components/DialogsContainer.tsx @@ -46,7 +46,7 @@ import ProjectTimeEntryFormDialog from '@/containers/Projects/containers/Project import ProjectExpenseForm from '@/containers/Projects/containers/ProjectExpenseForm'; import EstimatedExpenseFormDialog from '@/containers/Projects/containers/EstimatedExpenseFormDialog'; import ProjectInvoicingFormDialog from '@/containers/Projects/containers/ProjectInvoicingFormDialog'; - +import ProjectBillableEntriesFormDialog from '@/containers/Projects/containers/ProjectBillableEntriesFormDialog'; import { DialogsName } from '@/constants/dialogs'; /** @@ -134,6 +134,7 @@ export default function DialogsContainer() { + ); } diff --git a/src/constants/dialogs.ts b/src/constants/dialogs.ts index f3e97b41d..09cbfaa55 100644 --- a/src/constants/dialogs.ts +++ b/src/constants/dialogs.ts @@ -44,4 +44,5 @@ export enum DialogsName { ProjectExpenseForm = 'project-expense-form', EstimateExpenseForm = 'estimate-expense-form', ProjectInvoicingForm = 'project-invoicing-form', + ProjectBillableEntriesForm = 'project-billable-entries', } diff --git a/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesForm.schema.tsx b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesForm.schema.tsx new file mode 100644 index 000000000..7c3631e73 --- /dev/null +++ b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesForm.schema.tsx @@ -0,0 +1,6 @@ +import * as Yup from 'yup'; +import intl from 'react-intl-universal'; + +const Schema = Yup.object().shape({}); + +export const ProjectBillableEntriesFormSchema = Schema; diff --git a/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesForm.tsx b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesForm.tsx new file mode 100644 index 000000000..3dc19cf5e --- /dev/null +++ b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesForm.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { Formik } from 'formik'; +import { AppToaster } from '@/components'; +import { ProjectBillableEntriesFormSchema } from './ProjectBillableEntriesForm.schema'; +import ProjectBillableEntriesFormContent from './ProjectBillableEntriesFormContent'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; + +import { compose } from '@/utils'; + +const defaultInitialValues = {}; + +/** + * project billable entries form. + * @returns + */ +function ProjectBillableEntriesForm({ + //#withDialogActions + closeDialog, +}) { + const initialValues = { + ...defaultInitialValues, + }; + + // Handles the form submit. + const handleFormSubmit = (values, { setSubmitting, setErrors }) => { + const form = {}; + + // Handle request response success. + const onSuccess = (response) => { + AppToaster.show({}); + }; + + // Handle request response errors. + const onError = ({ + response: { + data: { errors }, + }, + }) => { + setSubmitting(false); + }; + }; + + return ( + + ); +} + +export default compose(withDialogActions)(ProjectBillableEntriesForm); diff --git a/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormContent.tsx b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormContent.tsx new file mode 100644 index 000000000..1b85dfad4 --- /dev/null +++ b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormContent.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { Form } from 'formik'; +import ProjectBillableEntriesFormFields from './ProjectBillableEntriesFormFields'; +import ProjectBillableEntriesFormFloatingActions from './ProjectBillableEntriesFormFloatingActions'; + +/** + * Project billable entries form content. + * @returns + */ +export default function ProjectBillableEntriesFormContent() { + return ( +
+ + + + ); +} diff --git a/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormDialogContent.tsx b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormDialogContent.tsx new file mode 100644 index 000000000..19e128e56 --- /dev/null +++ b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormDialogContent.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { ProjectBillableEntriesFormProvider } from './ProjectBillableEntriesFormProvider'; +import ProjectBillableEntriesForm from './ProjectBillableEntriesForm'; + +/** + * Project billable entries form dialog content. + * @returns + */ +export default function ProjectEntriesFormDialogContent({ + // #ownProps + dialogName, + projectId, +}) { + return ( + + + + ); +} diff --git a/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormFields.tsx b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormFields.tsx new file mode 100644 index 000000000..60ea2c538 --- /dev/null +++ b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormFields.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { useFormikContext } from 'formik'; +import { Classes } from '@blueprintjs/core'; +import { + FFormGroup, + FInputGroup, + FieldRequiredHint, + FormattedMessage as T, +} from '@/components'; +import { ProjectRowDivider, ProjectEntiresBox } from './components'; +import { useProjectBillableEntriesFormContext } from './ProjectBillableEntriesFormProvider'; + +/** + * Project billable entries form fields. + * @returns + */ +export default function ProjectBillableEntriesFormFields() { + // Formik context. + const { values } = useFormikContext(); + + const { billableEntries } = useProjectBillableEntriesFormContext(); + + return ( +
+ {/*------------ Filter by Date -----------*/} + } + labelInfo={} + > + + + + + + {/*------------ Filter by Type -----------*/} + } + labelInfo={} + > + + + + +
+ ); +} diff --git a/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormFloatingActions.tsx b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormFloatingActions.tsx new file mode 100644 index 000000000..cb0f6e331 --- /dev/null +++ b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormFloatingActions.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import styled from 'styled-components'; +import { useFormikContext } from 'formik'; +import { Intent, Button, Classes } from '@blueprintjs/core'; +import { FormattedMessage as T } from '@/components'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; +import { compose } from '@/utils'; + +/** + * project entries from floating actions. + * @return + */ +function ProjectEntriesFormFloatingActions({ + // #withDialogActions + closeDialog, +}) { + // Formik context. + const { isSubmitting } = useFormikContext(); + + // Handle close button click. + const handleCancelBtnClick = () => { + closeDialog(dialogName); + }; + + return ( +
+
+ + Save + +
+
+ ); +} + +export default compose(withDialogActions)(ProjectEntriesFormFloatingActions); + +const SaveButton = styled(Button)` + &.bp3-button { + min-width: 80px; + border-radius: 16px; + margin-left: 0px; + } +`; diff --git a/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormProvider.tsx b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormProvider.tsx new file mode 100644 index 000000000..d8845811d --- /dev/null +++ b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/ProjectBillableEntriesFormProvider.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { useProjectBillableEntries } from '../../hooks'; +import { DialogContent } from '@/components'; + +const ProjectBillableEntriesFormContext = React.createContext(); + +/** + * Project billable entries form provider. + * @returns + */ +function ProjectBillableEntriesFormProvider({ + // #ownProps + dialogName, + projectId, + ...props +}) { + // Handle fetch project billable entries. + const { + data: { billableEntries }, + isLoading: isProjectBillableEntriesLoading, + } = useProjectBillableEntries( + projectId, + { + billable_type: 'expense', + to_date: '', + }, + { + enabled: !!projectId, + keepPreviousData: true, + }, + ); + + //state provider. + const provider = { + dialogName, + projectId, + billableEntries, + }; + + return ( + + + + ); +} + +const useProjectBillableEntriesFormContext = () => + React.useContext(ProjectBillableEntriesFormContext); + +export { + ProjectBillableEntriesFormProvider, + useProjectBillableEntriesFormContext, +}; diff --git a/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/components.tsx b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/components.tsx new file mode 100644 index 000000000..4d938c890 --- /dev/null +++ b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/components.tsx @@ -0,0 +1,102 @@ +import React from 'react'; +import { Button } from '@blueprintjs/core'; +import intl from 'react-intl-universal'; +import styled from 'styled-components'; + +/** + * Projec billable entries item box. + * @returns + */ +function ProjectBillableEntriesItemBox({ projectBillableEntry }) { + return ( + + + {projectBillableEntry.title} + + {projectBillableEntry.date} + {projectBillableEntry.time} + + + + {projectBillableEntry.billable_amount} + + + Add + Show + + + ); +} + +/** + * Project billable entries box. + * @returns + */ +export function ProjectEntiresBox({ billableEntries }) { + return billableEntries.map((entries) => ( + + )); +} + +const ProjectEntryBox = styled.div` + display: flex; + flex-direction: column; + border-radius: 5px; + width: 360px; + height: 121px; + border: 1px solid #d4d9df; + padding: 15px 12px; + margin-bottom: 15px; + position: relative; +`; +const ProjectEntryHeader = styled.div``; +const ProjectEntryTitle = styled.div` + font-size: 14px; + line-height: 1.5; + font-weight: 500; + color: #444444; +`; +const ProjectEntrtyItemContent = styled.div` + display: flex; + justify-content: space-between; +`; +const ProjectEntryItem = styled.div` + font-weight: 400; + font-size: 10px; + color: #666666; +`; + +const ProjectEntryContent = styled.div` + flex: 1 0 auto; + line-height: 2rem; + border-bottom: 1px solid #e3e3e3; +`; + +const ProjectEntryAmount = styled.div` + font-size: 14px; + font-weight: 500; + color: #111111; +`; + +export const ProjectRowDivider = styled.div` + height: 1px; + background: #e3e3e3; + margin-bottom: 15px; + margin-top: 15px; +`; + +const ProjectEntryFoorer = styled.div` + padding: 0; +`; + +const ProjectEntryButton = styled(Button)` + &.bp3-button.bp3-small, + &.bp3-button:not([class*='bp3-intent-']):not(.bp3-minimal).bp3-small { + font-size: 12px; + color: #2172ed; + background: transparent; + &:last-child { + margin-right: 5px; + } + } +`; diff --git a/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/index.tsx b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/index.tsx new file mode 100644 index 000000000..b94ea65d7 --- /dev/null +++ b/src/containers/Projects/containers/ProjectBillableEntriesFormDialog/index.tsx @@ -0,0 +1,58 @@ +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 ProjectBillableEntriesFormDialogContent = React.lazy( + () => import('./ProjectBillableEntriesFormDialogContent'), +); + +/** + * Project billable entries form dialog. + * @returns + */ +function ProjectBillableEntriesFormDialog({ + dialogName, + payload: { projectId }, + isOpen, +}) { + return ( + } + isOpen={isOpen} + autoFocus={true} + canEscapeKeyClose={true} + style={{ width: '400px' }} + > + + + + + ); +} + +export default compose(withDialogRedux())(ProjectBillableEntriesFormDialog); + +const ProjectBillableEntriesFormDialogRoot = styled(Dialog)` + .bp3-dialog-body { + .bp3-form-group { + margin-bottom: 15px; + + label.bp3-label { + margin-bottom: 3px; + font-size: 13px; + } + } + } + .bp3-dialog-footer { + .bp3-dialog-footer-actions { + display: flex; + justify-content: flex-start; + } + } +`; diff --git a/src/containers/Projects/hooks/index.ts b/src/containers/Projects/hooks/index.ts index 544277616..3c94bde2a 100644 --- a/src/containers/Projects/hooks/index.ts +++ b/src/containers/Projects/hooks/index.ts @@ -1,3 +1,4 @@ export * from './projects' export * from './projectsTask' -export * from './projectTimeEntry' \ No newline at end of file +export * from './projectTimeEntry' +export * from './projectBillableEntries' \ No newline at end of file diff --git a/src/containers/Projects/hooks/projectBillableEntries.tsx b/src/containers/Projects/hooks/projectBillableEntries.tsx new file mode 100644 index 000000000..171e2cc1c --- /dev/null +++ b/src/containers/Projects/hooks/projectBillableEntries.tsx @@ -0,0 +1,27 @@ +import { useRequestQuery } from '@/hooks/useQueryRequest'; +import t from './type'; + +/** + * + * @param projectId - Project id. + * @param query + * @param props + * @returns + */ +export function useProjectBillableEntries(projectId, query, props) { + return useRequestQuery( + [t.PROJECT_BILLABLE_ENTRIES, projectId], + { + method: 'get', + url: `projects/${projectId}/billable/entries`, + params: query, + }, + { + select: (res) => res.data.billable_entries, + defaultData: { + billableEntries: [], + }, + ...props, + }, + ); +} diff --git a/src/containers/Projects/hooks/type.ts b/src/containers/Projects/hooks/type.ts index 72f12914c..f884e1246 100644 --- a/src/containers/Projects/hooks/type.ts +++ b/src/containers/Projects/hooks/type.ts @@ -18,9 +18,14 @@ const PROJECT_TIME_ENTRIES = { PROJECT_TIME_ENTRY: 'PROJECT_TIME_ENTRY', }; +const PROJECT_BILLABLE_ENTRIES = { + PROJECT_BILLABLE_ENTRIES: 'PROJECT_BILLABLE_ENTRIES', +}; + export default { ...PROJECTS, ...CUSTOMERS, ...PROJECT_TASKS, ...PROJECT_TIME_ENTRIES, + ...PROJECT_BILLABLE_ENTRIES, };