This commit is contained in:
Ahmed Bouhuolia
2020-03-16 00:06:15 +02:00
parent 56701951b7
commit 73711384f6
7925 changed files with 18478 additions and 959 deletions

View File

@@ -0,0 +1,51 @@
import React, {useMemo} from 'react';
import {
Intent,
Button,
} from '@blueprintjs/core';
import { FormattedList } from 'react-intl';
export default function MakeJournalEntriesFooter({
formik,
}) {
const creditSum = useMemo(() => {
return formik.values.entries.reduce((sum, entry) => {
return entry.credit + sum;
}, 0);
}, [formik.values.entries]);
const debitSum = useMemo(() => {
return formik.values.entries.reduce((sum, entry) => {
return entry.debit + sum;
}, 0);
}, [formik.values.entries]);
return (
<div>
<table>
<tbody>
<tr>
<td><strong>Total</strong></td>
<td>{ creditSum }</td>
<td>{ debitSum }</td>
</tr>
</tbody>
</table>
<div class="form__floating-footer">
<Button
intent={Intent.PRIMARY}
type="submit">
Save
</Button>
<Button
intent={Intent.PRIMARY}
type="submit"
className={'ml2'}>
Save & New
</Button>
</div>
</div>
);
}

View File

@@ -0,0 +1,93 @@
import React, {useState, useEffect} from 'react';
import * as Yup from 'yup';
import MakeJournalEntriesHeader from './MakeJournalEntriesHeader';
import MakeJournalEntriesFooter from './MakeJournalEntriesFooter';
import MakeJournalEntriesTable from './MakeJournalEntriesTable';
import {Formik, useFormik} from "formik";
import MakeJournalEntriesConnect from 'connectors/MakeJournalEntries.connect';
import AccountsConnect from 'connectors/Accounts.connector';
import DashboardConnect from 'connectors/Dashboard.connector';
import {compose} from 'utils';
import useAsync from 'hooks/async';
import moment from 'moment';
import AppToaster from 'components/AppToaster';
function MakeJournalEntriesForm({
makeJournalEntries,
fetchAccounts,
changePageTitle,
}) {
useEffect(() => {
changePageTitle('New Journal');
}, []);
const fetchHook = useAsync(async () => {
await Promise.all([
fetchAccounts(),
]);
});
const validationSchema = Yup.object().shape({
date: Yup.date().required(),
reference: Yup.string(),
description: Yup.string(),
entries: Yup.array().of(
Yup.object().shape({
credit: Yup.number().nullable(),
debit: Yup.number().nullable(),
}),
)
});
const defaultEntry = {
account_id: null,
credit: null,
debit: null,
note: '',
};
const formik = useFormik({
enableReinitialize: true,
validationSchema,
initialValues: {
reference: '',
date: moment(new Date()).format('YYYY-MM-DD'),
description: '',
entries: [
defaultEntry,
defaultEntry,
defaultEntry,
defaultEntry,
],
},
onSubmit: (values) => {
const form = values.entries.filter((entry) => (
(entry.credit || entry.debit)
));
makeJournalEntries(values).then((response) => {
AppToaster.show({
message: 'the_account_has_been_submit',
});
}).catch((error) => {
});
},
});
console.log(formik.errors);
return (
<form onSubmit={formik.handleSubmit}>
<MakeJournalEntriesHeader formik={formik} />
<MakeJournalEntriesTable formik={formik} />
<MakeJournalEntriesFooter formik={formik} />
</form>
);
}
export default compose(
MakeJournalEntriesConnect,
AccountsConnect,
DashboardConnect,
)(MakeJournalEntriesForm);

View File

@@ -0,0 +1,75 @@
import React from 'react';
import * as Yup from 'yup';
import {
InputGroup,
FormGroup,
Intent,
Position,
} from '@blueprintjs/core';
import {DatePicker, DateInput} from '@blueprintjs/datetime';
import {Formik, useFormik} from "formik";
import {useIntl} from 'react-intl';
import {Row, Col} from 'react-grid-system';
import moment from 'moment';
import {momentFormatter} from 'utils';
export default function MakeJournalEntriesHeader({
formik
}) {
const intl = useIntl();
const handleDateChange = (date) => {
const formatted = moment(date).format('YYYY-MM-DD');
formik.setFieldValue('date', formatted);
};
return (
<div class="make-journal-entries__header">
<Row>
<Col sm={4}>
<FormGroup
label={intl.formatMessage({'id': 'reference'})}
className={'form-group--reference'}
intent={formik.errors.name && Intent.DANGER}
helperText={formik.errors.name && formik.errors.label}
fill={true}>
<InputGroup
intent={formik.errors.name && Intent.DANGER}
fill={true}
{...formik.getFieldProps('reference')} />
</FormGroup>
</Col>
<Col sm={3}>
<FormGroup
label={intl.formatMessage({'id': 'date'})}
intent={formik.errors.date && Intent.DANGER}
helperText={formik.errors.date && formik.errors.date}
minimal={true}>
<DateInput
{...momentFormatter('YYYY/MM/DD')}
defaultValue={new Date()}
onChange={handleDateChange}
popoverProps={{ position: Position.BOTTOM }} />
</FormGroup>
</Col>
<Col sm={4}>
<FormGroup
label={intl.formatMessage({'id': 'description'})}
className={'form-group--description'}
intent={formik.errors.name && Intent.DANGER}
helperText={formik.errors.name && formik.errors.label}
fill={true}>
<InputGroup
intent={formik.errors.name && Intent.DANGER}
fill={true}
{...formik.getFieldProps('description')} />
</FormGroup>
</Col>
</Row>
</div>
);
}

View File

@@ -0,0 +1,14 @@
import React from 'react';
import MakeJournalEntriesForm from './MakeJournalEntriesForm';
import DashboardConnect from 'connectors/Dashboard.connector';
import {compose} from 'utils';
function MakeJournalEntriesPage() {
return (
<MakeJournalEntriesForm />
);
}
export default compose(
DashboardConnect,
)(MakeJournalEntriesPage);

View File

@@ -0,0 +1,206 @@
import React, {useState, useMemo} from 'react';
import DataTable from 'components/DataTable';
import {
FormGroup,
InputGroup,
MenuItem,
Button,
Intent,
} from '@blueprintjs/core';
import {Select} from '@blueprintjs/select';
import Icon from 'components/Icon';
import AccountsConnect from 'connectors/Accounts.connector.js';
import {compose} from 'utils';
function MakeJournalEntriesTable({
formik,
accounts,
}) {
const [selectAccountsState, setSelectedAccount] = useState(false);
// Account item of select accounts field.
const accountItem = (item, { handleClick, modifiers, query }) => {
return (<MenuItem text={item.name} label={item.code} key={item.id} onClick={handleClick} />)
};
// Filters accounts options by account name or code.
const filterAccountsPredicater = (query, account, _index, exactMatch) => {
const normalizedTitle = account.name.toLowerCase();
const normalizedQuery = query.toLowerCase();
if (exactMatch) {
return normalizedTitle === normalizedQuery;
} else {
return `${account.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
}
};
// Handle account field change.
const onChangeAccount = (index) => (account) => {
setSelectedAccount({
...selectAccountsState,
[index + 1]: account,
});
formik.setFieldValue(`entries[${index}].account_id`, account.id);
};
const [data, setData] = useState([
...formik.values.entries,
]);
const updateData = (rowIndex, columnId, value) => {
setData((old) =>
old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value,
}
}
return row
})
)
}
const CellInputGroupRenderer = ({
row: { index },
column: { id },
cell: { value: initialValue },
}) => {
const [state, setState] = useState(initialValue);
return (
<InputGroup
fill={true}
value={state}
onChange={(event) => {
setState(event.target.value);
}}
onBlur={(event) => {
formik.setFieldValue(`entries[${index}].[${id}]`, event.target.value);
updateData(index, id, state);
}}
/>);
}
const ActionsCellRenderer = ({
row: { index },
column: { id },
cell: { value: initialValue },
}) => {
const onClickRemoveRole = () => {
};
return (
<Button
icon={<Icon icon="mines" />}
iconSize={14}
className="ml2"
minimal={true}
intent={Intent.DANGER}
onClick={onClickRemoveRole()} />
);
}
const columns = useMemo(() => [
{
Header: '#',
accessor: 'index',
Cell: ({ row: {index} }) => (
<span>{ index + 1 }</span>
),
className: "actions",
},
{
Header: 'Account',
accessor: 'account',
Cell: ({ row: { index } }) => (
<FormGroup
className="{'form-group--account'}"
inline={true}>
<Select
items={accounts}
noResults={<MenuItem disabled={true} text="No results." />}
itemRenderer={accountItem}
itemPredicate={filterAccountsPredicater}
popoverProps={{ minimal: true }}
onItemSelect={onChangeAccount(index)}>
<Button
rightIcon="caret-down"
text={(selectAccountsState[(index + 1)])
? selectAccountsState[(index + 1)].name : "Select Account"}
/>
</Select>
</FormGroup>
),
className: "account",
},
{
Header: 'Note',
accessor: 'note',
Cell: CellInputGroupRenderer,
className: "note",
},
{
Header: 'Credit',
accessor: 'credit',
Cell: CellInputGroupRenderer,
className: "credit",
},
{
Header: 'Debit',
accessor: 'debit',
Cell: CellInputGroupRenderer,
className: "debit",
},
{
Header: '',
accessor: 'action',
Cell: ActionsCellRenderer,
className: "actions",
}
]);
const onClickNewRow = () => {
setData([
...data,
{
credit: 0,
debit: 0,
account_id: null,
note: '',
}
]);
}
return (
<div class="make-journal-entries__table">
<DataTable
columns={columns}
data={data} />
<div class="mt1">
<Button
minimal={true}
intent={Intent.PRIMARY}
onClick={onClickNewRow}>
+ New Entry
</Button>
<Button
minimal={true}
intent={Intent.PRIMARY}
onClick={onClickNewRow}>
- Clear all entries
</Button>
</div>
</div>
);
}
export default compose(
AccountsConnect,
)(MakeJournalEntriesTable);