https://github.com/ivov/monotax
Colorful dashboard to display financial stats for a side business, built with TypeScript/React, TypeScript/Node and SQLite.
https://github.com/ivov/monotax
material-ui nodejs reactjs sqlite typescript
Last synced: about 2 months ago
JSON representation
Colorful dashboard to display financial stats for a side business, built with TypeScript/React, TypeScript/Node and SQLite.
- Host: GitHub
- URL: https://github.com/ivov/monotax
- Owner: ivov
- License: mit
- Created: 2020-02-18T23:11:12.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2020-04-18T18:33:20.000Z (about 6 years ago)
- Last Synced: 2025-01-02T06:16:20.701Z (over 1 year ago)
- Topics: material-ui, nodejs, reactjs, sqlite, typescript
- Language: TypeScript
- Homepage:
- Size: 11.3 MB
- Stars: 0
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# monotax
[](https://opensource.org/licenses/MIT)
Colorful dashboard to display financial stats for a side business, built with TypeScript/React, TypeScript/Node and SQLite.
## Overview
Quick app to display a client's earnings, expenses, savings and invoices in material design, including a PDF invoice parser.
Features:
- Views for all-time, quarterly, yearly and monthly stats
- Views for earnings, expenses, savings and invoices
- CLI utility for quick PDF-to-SQLite conversion
- Colorful animated charts and tabbed tables
- Expanding panels, pagination and sorting
## Screenshots
## Operation
PDF invoices are placed at `data/pdf` and parsed with `npm run parse`. The invoices must be formatted as those issued under the monotax scheme of Argentina's Federal Administration of Public Revenue.
Every PDF is converted to a JSON object and is persisted in the database at `data/sql/monotax.db`. The db is pre-populated with earnings, expenses and ARS-USD exchange rate data, queriable through 35+ SQL views, many of which are used by the backend. A demo db and its full schema are included.
PDF invoice sample:
JSON object sample:
```js
{
date: "Mon Dec 23 2019 12:41:30 GMT-0300 (Argentina Standard Time)",
id: "692",
name: "Lorem Ipsum",
address: {
street: "Dolor Sit",
province: "Amet",
}
client_id: {
type: "DNI",
number: "12934612",
}
vatStatus: "Consumidor Final",
amount: 1900.00
}
```
The frontend relies on [React Admin](https://github.com/marmelab/react-admin) to auto-fetch data for views corresponding to earnings, expenses, savings and invoices. The client also makes requests custom endpoints to fetch data for the global overview and the yearly stats views.
```ts
// setAutomaticRoutes.ts → four React Admin endpoints
const setAutomaticRoutes = (app: express.Application) => {
const autoRoutes = ["invoices", "earnings", "expenses", "savings"];
autoRoutes.forEach(route =>
app.get(
"/api/" + route,
(request: express.Request, response: express.Response) =>
createAutomaticRoute(request, response, route)
)
);
};
const createAutomaticRoute = (
request: express.Request,
response: express.Response,
route: string
) => {
const { offset, limit } = request.query;
const [field, order] = JSON.parse(request.query.sort);
const results = DatabaseService.getAllRecords(
VIEWS[route],
offset,
limit,
field,
order
);
const total = DatabaseService.getCount(VIEWS[route]);
return response
.status(200)
.set("Content-Range", route + "0-15/" + total)
.set("Access-Control-Expose-Headers", "Content-Range")
.json(results);
};
```
```ts
// dataProvider.ts → four API calls
getList: (resource: string, params: GetListParams) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
filter: JSON.stringify(params.filter),
offset: JSON.stringify((page - 1) * perPage),
limit: JSON.stringify(perPage),
sort: JSON.stringify([field, order])
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ json, headers }: any) => {
return {
data: json,
total: parseInt(
headers
.get("Content-Range")
.split("/")
.pop(),
10
)
};
});
};
```
```ts
// setCustomRoutes.ts → four custom endpoints
const setCustomRoutes = (app: express.Application) => {
app.get("/api/overview/", (request, response) => {
const quarterlyData = DatabaseService.getQuarterlyData();
const allTimeTotals = DatabaseService.getAllTimeTotals();
const yearlyTotals = DatabaseService.getYearlyTotals();
const monthlyAveragesPerYear = DatabaseService.getMonthlyAveragesPerYear();
const lastSixMonthsValues = DatabaseService.getLastSixMonthsValues();
response.status(200).json({
quarterlyData,
allTimeTotals,
yearlyTotals,
monthlyAveragesPerYear,
lastSixMonthsValues
});
});
const years = ["2017", "2018", "2019"];
years.forEach(year =>
app.get("/api/year/" + year, (request, response) => {
const yearData = DatabaseService.getYearData(year);
const totalsForYear = calculateTotals(yearData);
response.status(200).json({
earningsForYear: yearData["earnings"],
expensesForYear: yearData["expenses"],
savingsForYear: yearData["savings"],
invoicedForYear: yearData["invoiced"],
totalsForYear
});
})
);
};
```
The dashboard borrows from [Material UI](https://material-ui.com/) icons and Material React components, to display data in colorful boxes, animated charts and tabbed tables. Material React components must be placed at `client/src/mdr` and are not included in this repo.
A dev proxy is set up so that any request from the client at `http://localhost:3000/` made to an `/api/` endpoint is redirected to the server at `http://localhost:5000/`. See the CRA docs on [API request proxying](https://create-react-app.dev/docs/proxying-api-requests-in-development/).
```js
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function(app) {
app.use(
"/api",
createProxyMiddleware({
target: "http://localhost:5000",
changeOrigin: true
})
);
};
```
## Author
© 2020 Iván Ovejero
## License
Distributed under the MIT License. See [LICENSE.md](LICENSE.md)