feat: add Project invoicing form dialog.

This commit is contained in:
elforjani13
2022-09-08 21:43:34 +02:00
parent d77fcb7c89
commit c75f46d8a4
14 changed files with 317 additions and 5 deletions

View File

@@ -45,6 +45,7 @@ import ProjectTaskFormDialog from '@/containers/Projects/containers/ProjectTaskF
import ProjectTimeEntryFormDialog from '@/containers/Projects/containers/ProjectTimeEntryFormDialog';
import ProjectExpenseForm from '@/containers/Projects/containers/ProjectExpenseForm';
import EstimatedExpenseFormDialog from '@/containers/Projects/containers/EstimatedExpenseFormDialog';
import ProjectInvoicingFormDialog from '@/containers/Projects/containers/ProjectInvoicingFormDialog';
import { DialogsName } from '@/constants/dialogs';
@@ -130,6 +131,9 @@ export default function DialogsContainer() {
<EstimatedExpenseFormDialog
dialogName={DialogsName.EstimateExpenseForm}
/>
<ProjectInvoicingFormDialog
dialogName={DialogsName.ProjectInvoicingForm}
/>
</div>
);
}

View File

@@ -43,4 +43,5 @@ export enum DialogsName {
ProjectTimeEntryForm = 'project-time-entry-form',
ProjectExpenseForm = 'project-expense-form',
EstimateExpenseForm = 'estimate-expense-form',
ProjectInvoicingForm = 'project-invoicing-form',
}

View File

@@ -121,7 +121,7 @@ export function useExpenseFormTableColumns({ landedCost }) {
disableSortBy: true,
width: 100,
},
...(featureCan(Features.Branches)
...(featureCan(Features.Projects)
? [
{
Header: intl.get('project'),

View File

@@ -1,6 +1,7 @@
import intl from 'react-intl-universal';
export const projectTranslations = [
{ name: intl.get('project_details.new_invoicing'), path: 'invoincing' },
{ name: intl.get('project_details.new_expense'), path: 'expense' },
{
name: intl.get('project_details.new_estimated_expense'),

View File

@@ -0,0 +1,13 @@
import * as Yup from 'yup';
import intl from 'react-intl-universal';
const Schema = Yup.object().shape({
date: Yup.date()
.label(intl.get('project_invocing.schema.label.date'))
.required(),
time: Yup.boolean(),
unbilled: Yup.boolean(),
bills: Yup.boolean(),
});
export const CreateProjectInvoicingFormSchema = Schema;

View File

@@ -0,0 +1,59 @@
import React from 'react';
import moment from 'moment';
import intl from 'react-intl-universal';
import { Formik } from 'formik';
import { Intent } from '@blueprintjs/core';
import { AppToaster } from '@/components';
import ProjectInvoicingFormContent from './ProjectInvoicingFormContent';
import { CreateProjectInvoicingFormSchema } from './ProjectInvoicingForm.schema';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { compose } from '@/utils';
const defaultInitialValues = {
date: moment(new Date()).format('YYYY-MM-DD'),
time: false,
unbilled: false,
bills: false,
};
/**
* project invoicing form.
* @returns
*/
function ProjectInvoicingForm({
// #withDialogActions
closeDialog,
}) {
// Initial form values
const initialValues = {
...defaultInitialValues,
};
// Handles the form submit.
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
// Handle request response success.
const onSuccess = (response) => {};
// Handle request response errors.
const onError = ({
response: {
data: { errors },
},
}) => {
setSubmitting(false);
};
};
return (
<Formik
validationSchema={CreateProjectInvoicingFormSchema}
initialValues={initialValues}
onSubmit={handleFormSubmit}
component={ProjectInvoicingFormContent}
/>
);
}
export default compose(withDialogActions)(ProjectInvoicingForm);

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { Form } from 'formik';
import ProjectInvoicingFormFields from './ProjectInvoicingFormFields';
import ProjectInvoicingFormFloatingActions from './ProjectInvoicingFormFloatingActions';
/**
* Project Invoicing form content.
*/
export default function ProjectInvoicingFormContent() {
return (
<Form>
<ProjectInvoicingFormFields />
<ProjectInvoicingFormFloatingActions />
</Form>
);
}

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { ProjectInvoicingFormProvider } from './ProjectInvoicingFormProvider';
import ProjectInvoicingForm from './ProjectInvoicingForm';
/**
* Project Invoicing form dialog content.
* @returns
*/
export default function ProjectInvoicingFormDialogContent({
// #ownProps
dialogName,
}) {
return (
<ProjectInvoicingFormProvider dialogName={dialogName}>
<ProjectInvoicingForm />
</ProjectInvoicingFormProvider>
);
}

View File

@@ -0,0 +1,60 @@
import React from 'react';
import intl from 'react-intl-universal';
import classNames from 'classnames';
import { CLASSES } from '@/constants/classes';
import { Classes, Position, FormGroup, ControlGroup } from '@blueprintjs/core';
import {
FFormGroup,
FCheckbox,
FDateInput,
FieldRequiredHint,
} from '@/components';
import { momentFormatter } from '@/utils';
/**
* Project invoicing form fields.
* @returns
*/
function ProjectInvoicingFormFields() {
return (
<div className={Classes.DIALOG_BODY}>
{/*------------ Date -----------*/}
<FFormGroup
label={intl.get('project_invoicing.dialog.bill_to')}
name={'date'}
className={classNames(CLASSES.FILL, 'form-group--date')}
>
<FDateInput
{...momentFormatter('YYYY/MM/DD')}
name="date"
formatDate={(date) => date.toLocaleString()}
popoverProps={{
position: Position.BOTTOM,
minimal: true,
}}
/>
</FFormGroup>
<FFormGroup name={'time'}>
{/*------------ All time entreis -----------*/}
<FCheckbox
name="time"
label={intl.get('project_invoicing.dialog.all_time_entries')}
/>
{/*------------ All unbilled expenses -----------*/}
<FCheckbox
name="unbilled"
label={intl.get('project_invoicing.dialog.all_unbilled_expenses')}
/>
{/*------------ All bills. -----------*/}
<FCheckbox
name="bills"
label={intl.get('project_invoicing.dialog.all_bills')}
/>
</FFormGroup>
</div>
);
}
export default ProjectInvoicingFormFields;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import { useFormikContext } from 'formik';
import { Intent, Button, Classes } from '@blueprintjs/core';
import { FormattedMessage as T } from '@/components';
import { useProjectInvoicingFormContext } from './ProjectInvoicingFormProvider';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import { compose } from '@/utils';
/**
* Project invoicing from floating actions
* @returns
*/
function ProjectInvoicingFormFloatingActions({
// #withDialogActions
closeDialog,
}) {
// Formik context.
const { isSubmitting } = useFormikContext();
// project invoicing form dialog context.
const { dialogName } = useProjectInvoicingFormContext();
// Handle close button click.
const handleCancelBtnClick = () => {
closeDialog(dialogName);
};
return (
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button onClick={handleCancelBtnClick} style={{ minWidth: '85px' }}>
<T id={'cancel'} />
</Button>
<Button
intent={Intent.PRIMARY}
loading={isSubmitting}
style={{ minWidth: '75px' }}
type="submit"
>
<T id={'project_invoicing.label.add'} />
</Button>
</div>
</div>
);
}
export default compose(withDialogActions)(ProjectInvoicingFormFloatingActions);

View File

@@ -0,0 +1,30 @@
import React from 'react';
import { DialogContent } from '@/components';
const ProjectInvoicingFormContext = React.createContext();
/**
* Project invoicing form provider.
* @returns
*/
function ProjectInvoicingFormProvider({
// #ownProps
dialogName,
...props
}) {
// State provider.
const provider = {
dialogName,
};
return (
<DialogContent isLoading={false}>
<ProjectInvoicingFormContext.Provider value={provider} {...props} />
</DialogContent>
);
}
const useProjectInvoicingFormContext = () =>
React.useContext(ProjectInvoicingFormContext);
export { ProjectInvoicingFormProvider, useProjectInvoicingFormContext };

View File

@@ -0,0 +1,54 @@
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 ProjectInvoicingDialogContent = React.lazy(
() => import('./ProjectInvoicingFormDialogContent'),
);
/**
* Project invoicing form dialog.
* @returns
*/
function ProjectInvoicingFormDialog({ dialogName, payload: {}, isOpen }) {
return (
<ProjectInvoicingFormDialogRoot
name={dialogName}
title={<T id={'project_invoicing.dialog.project_invoicing'} />}
isOpen={isOpen}
autoFocus={true}
canEscapeKeyClose={true}
style={{ width: '370px' }}
>
<DialogSuspense>
<ProjectInvoicingDialogContent dialogName={dialogName} />
</DialogSuspense>
</ProjectInvoicingFormDialogRoot>
);
}
export default compose(withDialogRedux())(ProjectInvoicingFormDialog);
const ProjectInvoicingFormDialogRoot = styled(Dialog)`
.bp3-dialog-body {
.bp3-form-group {
margin-bottom: 15px;
margin-top: 15px;
label.bp3-label {
margin-bottom: 3px;
font-size: 13px;
}
}
label.bp3-control.bp3-checkbox {
margin-top: 15px;
}
}
.bp3-dialog-footer {
padding-top: 10px;
}
`;

View File

@@ -174,7 +174,7 @@ export const useProjectsListColumns = () => {
id: 'status',
Header: '',
accessor: StatusAccessor,
width: 50,
width: 40,
className: 'status',
},
],

View File

@@ -2108,6 +2108,7 @@
"project_details.label.purchases": "Purchases",
"project_details.label.sales": "Sales",
"project_details.label.journals": "Journals",
"project_details.new_invoicing": "New Invoicing",
"project_details.new_expense": "New Expense",
"project_details.new_estimated_expense": "New Estimated Expense",
"timesheets.action.delete_timesheet": "Delete",
@@ -2191,8 +2192,8 @@
"bill.project_name.label": "Project Name",
"payment_receive.project_name.label": "Project Name",
"select_project": "Select project",
"project":"Project",
"projects_multi_select.label":"Projects",
"project": "Project",
"projects_multi_select.label": "Projects",
"projects_multi_select.placeholder": "Filter by projects…",
"project_profitability_summary": "Project Profitability Summary",
"project_profitability_summary.filter_projects.all_projects": "All Projects",
@@ -2201,5 +2202,11 @@
"project_profitability_summary.filter_projects.without_zero_balance.hint": "Include projects that onces have transactions on the given date period only.",
"project_profitability_summary.filter_projects.with_transactions": "Projects with transactions",
"project_profitability_summary.filter_projects.with_transactions.hint": "Include projects that onces have transactions on the given date period only.",
"project_profitability_summary.filter_options.label": "Filter projects"
"project_profitability_summary.filter_options.label": "Filter projects",
"project_invoicing.label.add": "Add",
"project_invoicing.dialog.project_invoicing": "Project Invoicing",
"project_invoicing.dialog.all_time_entries": "All time entries",
"project_invoicing.dialog.all_unbilled_expenses": "All unbilled expenses",
"project_invoicing.dialog.all_bills": "All bills",
"project_invoicing.dialog.bill_to": "Bill To"
}