├── client ├── .env ├── src │ ├── assets │ │ ├── soho-dark │ │ │ └── fonts │ │ │ │ ├── lato-v17-latin-ext_latin-300.woff │ │ │ │ ├── lato-v17-latin-ext_latin-700.woff │ │ │ │ ├── lato-v17-latin-ext_latin-300.woff2 │ │ │ │ ├── lato-v17-latin-ext_latin-700.woff2 │ │ │ │ ├── lato-v17-latin-ext_latin-regular.woff │ │ │ │ └── lato-v17-latin-ext_latin-regular.woff2 │ │ ├── soho-light │ │ │ └── fonts │ │ │ │ ├── lato-v17-latin-ext_latin-300.woff │ │ │ │ ├── lato-v17-latin-ext_latin-300.woff2 │ │ │ │ ├── lato-v17-latin-ext_latin-700.woff │ │ │ │ ├── lato-v17-latin-ext_latin-700.woff2 │ │ │ │ ├── lato-v17-latin-ext_latin-regular.woff │ │ │ │ └── lato-v17-latin-ext_latin-regular.woff2 │ │ ├── favicon.svg │ │ └── logo.svg │ ├── main.jsx │ ├── i18n │ │ ├── i18n.js │ │ ├── translationEN.json │ │ ├── translationES.json │ │ ├── translationIT.json │ │ ├── translationDE.json │ │ └── translationFR.json │ ├── Footer.jsx │ ├── finances │ │ ├── components │ │ │ └── ResumeAccounts.jsx │ │ ├── buckets │ │ │ ├── ListBucket.jsx │ │ │ ├── EditBucket.jsx │ │ │ ├── CreateBucket.jsx │ │ │ └── Buckets.jsx │ │ ├── accounts │ │ │ ├── Accounts.jsx │ │ │ ├── AddAccounts.jsx │ │ │ └── EditAccount.jsx │ │ ├── transactions │ │ │ ├── ListTransaction.jsx │ │ │ └── AddTransactions.jsx │ │ ├── vaults │ │ │ ├── ListVault.jsx │ │ │ ├── ListVaultTransactions.jsx │ │ │ ├── Vaults.jsx │ │ │ ├── CreateVault.jsx │ │ │ └── EditVault.jsx │ │ ├── expense │ │ │ ├── ListExpenses.jsx │ │ │ └── AddExpense.jsx │ │ └── Listing.jsx │ ├── App.jsx │ ├── index.css │ └── Layout.jsx ├── vite.config.js ├── Dockerfile ├── .gitignore ├── README.md ├── .eslintrc.cjs ├── index.html ├── package.json ├── nginx.conf └── public │ └── vite.svg ├── server ├── Dockerfile ├── controllers │ ├── category │ │ ├── addCategory.controller.js │ │ └── allCategory.controller.js │ ├── notifications │ │ └── notifications.controller.js │ ├── vaults │ │ ├── vaultList.controller.js │ │ ├── transactionVaultList.controller.js │ │ ├── createVault.controller.js │ │ ├── editVault.controller.js │ │ └── vault.controller.js │ ├── buckets │ │ ├── listBucket.controller.js │ │ ├── editBucket.controller.js │ │ └── buckets.controller.js │ ├── expense │ │ ├── expensesList.controller.js │ │ └── addExpense.controller.js │ ├── transactions │ │ ├── transactionsList.controller.js │ │ ├── transactions.controller.js │ │ └── transactionVault.controller.js │ ├── edit │ │ ├── deleteBucket.controller.js │ │ ├── deleteVault.controller.js │ │ ├── deleteAccount.controller.js │ │ ├── deleteExpense.controller.js │ │ ├── deleteTransaction.controller.js │ │ ├── deleteTransactionVault.controller.js │ │ ├── vaultCell.controller.js │ │ └── bucketCell.controller.js │ └── accounts │ │ ├── accounts.controller.js │ │ └── editAccount.controller.js ├── package.json ├── models │ ├── monthlyVaults.js │ ├── accounts.js │ ├── vaults.js │ ├── monthlyBuckets.js │ ├── expenses.js │ ├── transactionsVaults.js │ ├── buckets.js │ └── transactions.js ├── server.js └── routes │ ├── edit.js │ └── finance.js ├── docker-compose.yml ├── .gitignore └── README.md /client/.env: -------------------------------------------------------------------------------- 1 | VITE_BACKEND_ADRESS=http://localhost 2 | VITE_BACKEND_PORT=3000 -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18 2 | WORKDIR /app 3 | COPY package*.json ./ 4 | RUN npm install 5 | COPY . . 6 | EXPOSE 3000 7 | CMD ["node", "server.js"] -------------------------------------------------------------------------------- /client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-300.woff -------------------------------------------------------------------------------- /client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-700.woff -------------------------------------------------------------------------------- /client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-300.woff2 -------------------------------------------------------------------------------- /client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-700.woff2 -------------------------------------------------------------------------------- /client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-300.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-300.woff -------------------------------------------------------------------------------- /client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-300.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-300.woff2 -------------------------------------------------------------------------------- /client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-700.woff -------------------------------------------------------------------------------- /client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-700.woff2 -------------------------------------------------------------------------------- /client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-regular.woff -------------------------------------------------------------------------------- /client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-dark/fonts/lato-v17-latin-ext_latin-regular.woff2 -------------------------------------------------------------------------------- /client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-regular.woff -------------------------------------------------------------------------------- /client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KayatoSan/Budget5S/HEAD/client/src/assets/soho-light/fonts/lato-v17-latin-ext_latin-regular.woff2 -------------------------------------------------------------------------------- /client/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /server/controllers/category/addCategory.controller.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const get = async (req, res, next) => { 4 | 5 | }; 6 | 7 | const post = async (req, res, next) => { 8 | 9 | }; 10 | 11 | module.exports = { 12 | get, 13 | post, 14 | }; 15 | -------------------------------------------------------------------------------- /server/controllers/category/allCategory.controller.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const get = async (req, res, next) => { 4 | 5 | }; 6 | 7 | const post = async (req, res, next) => { 8 | 9 | }; 10 | 11 | module.exports = { 12 | get, 13 | post, 14 | }; 15 | -------------------------------------------------------------------------------- /server/controllers/notifications/notifications.controller.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | const get = async (req, res, next) => { 4 | 5 | }; 6 | 7 | const post = async (req, res, next) => { 8 | 9 | 10 | }; 11 | 12 | module.exports = { 13 | get, 14 | post, 15 | }; 16 | -------------------------------------------------------------------------------- /client/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18 as build 2 | WORKDIR /app 3 | COPY package*.json ./ 4 | RUN npm install 5 | COPY . . 6 | RUN npm run build 7 | 8 | FROM nginx:alpine 9 | COPY nginx.conf /etc/nginx/conf.d/default.conf 10 | COPY --from=build /app/dist /usr/share/nginx/html 11 | EXPOSE 80 12 | CMD ["nginx", "-g", "daemon off;"] -------------------------------------------------------------------------------- /server/controllers/vaults/vaultList.controller.js: -------------------------------------------------------------------------------- 1 | const dbVaults = require("../../models/vaults"); 2 | 3 | const get = async (req, res, next) => { 4 | const data = await dbVaults.find() 5 | res.send({ 6 | data 7 | }) 8 | }; 9 | 10 | const post = async (req, res, next) => { 11 | 12 | }; 13 | 14 | module.exports = { 15 | get, 16 | post, 17 | }; 18 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /server/controllers/buckets/listBucket.controller.js: -------------------------------------------------------------------------------- 1 | const dbBuckets = require("../../models/buckets"); 2 | 3 | const get = async (req, res, next) => { 4 | const data = await dbBuckets.find() 5 | res.send({ 6 | data 7 | }) 8 | }; 9 | 10 | const post = async (req, res, next) => { 11 | 12 | }; 13 | 14 | module.exports = { 15 | get, 16 | post, 17 | }; 18 | -------------------------------------------------------------------------------- /server/controllers/expense/expensesList.controller.js: -------------------------------------------------------------------------------- 1 | const dbExpenses = require("../../models/expenses"); 2 | 3 | const get = async (req, res, next) => { 4 | const data = await dbExpenses.find() 5 | res.send({ 6 | data 7 | }) 8 | }; 9 | 10 | const post = async (req, res, next) => { 11 | 12 | }; 13 | 14 | module.exports = { 15 | get, 16 | post, 17 | }; 18 | -------------------------------------------------------------------------------- /server/controllers/transactions/transactionsList.controller.js: -------------------------------------------------------------------------------- 1 | const dbTransactions = require("../../models/transactions"); 2 | 3 | const get = async (req, res, next) => { 4 | const data = await dbTransactions.find() 5 | res.send({ 6 | data 7 | }) 8 | }; 9 | 10 | const post = async (req, res, next) => { 11 | 12 | }; 13 | 14 | module.exports = { 15 | get, 16 | post, 17 | }; 18 | -------------------------------------------------------------------------------- /server/controllers/vaults/transactionVaultList.controller.js: -------------------------------------------------------------------------------- 1 | const dbTransactionsVault = require("../../models/transactionsVaults"); 2 | 3 | const get = async (req, res, next) => { 4 | const data = await dbTransactionsVault.find() 5 | res.send({ 6 | data 7 | }) 8 | }; 9 | 10 | const post = async (req, res, next) => { 11 | 12 | }; 13 | 14 | module.exports = { 15 | get, 16 | post, 17 | }; 18 | -------------------------------------------------------------------------------- /server/controllers/edit/deleteBucket.controller.js: -------------------------------------------------------------------------------- 1 | const dbBuckets = require('../../models/buckets') 2 | 3 | const post = async (req, res, next) => { 4 | console.log(req.params.id, "was deleted") 5 | try { 6 | await dbBuckets.findByIdAndDelete(req.params.id) 7 | next() 8 | } catch (err) { 9 | console.error(err) 10 | next() 11 | } 12 | }; 13 | 14 | module.exports = { 15 | post, 16 | }; 17 | -------------------------------------------------------------------------------- /server/controllers/edit/deleteVault.controller.js: -------------------------------------------------------------------------------- 1 | const dbVaults = require('../../models/vaults') 2 | 3 | const post = async (req, res, next) => { 4 | console.log(req.params.id, "was deleted") 5 | try { 6 | await dbVaults.findByIdAndDelete(req.params.id) 7 | next() 8 | } catch (err) { 9 | console.error(err) 10 | next() 11 | } 12 | }; 13 | 14 | module.exports = { 15 | post, 16 | }; 17 | -------------------------------------------------------------------------------- /server/controllers/edit/deleteAccount.controller.js: -------------------------------------------------------------------------------- 1 | const dbAccounts = require('../../models/accounts') 2 | 3 | const post = async (req, res, next) => { 4 | console.log(req.params.id, "was deleted") 5 | try { 6 | await dbAccounts.findByIdAndDelete(req.params.id) 7 | next() 8 | } catch (err) { 9 | console.error(err) 10 | next() 11 | } 12 | }; 13 | 14 | module.exports = { 15 | post, 16 | }; 17 | -------------------------------------------------------------------------------- /server/controllers/edit/deleteExpense.controller.js: -------------------------------------------------------------------------------- 1 | const dbExpenses = require('../../models/expenses') 2 | 3 | const post = async (req, res, next) => { 4 | try { 5 | await dbExpenses.findByIdAndDelete(req.params.id) 6 | console.log(req.params.id, "was deleted") 7 | next() 8 | } catch (err) { 9 | console.error(err) 10 | next() 11 | } 12 | }; 13 | 14 | module.exports = { 15 | post, 16 | }; -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | client: 4 | build: 5 | context: ./client 6 | dockerfile: Dockerfile 7 | ports: 8 | - "8000:80" 9 | depends_on: 10 | - server 11 | server: 12 | build: 13 | context: ./server 14 | dockerfile: Dockerfile 15 | ports: 16 | - "3000:3000" 17 | depends_on: 18 | - mongo 19 | mongo: 20 | image: mongo 21 | ports: 22 | - "27017:27017" -------------------------------------------------------------------------------- /server/controllers/edit/deleteTransaction.controller.js: -------------------------------------------------------------------------------- 1 | const dbTransactions = require('../../models/transactions') 2 | 3 | const post = async (req, res, next) => { 4 | try { 5 | await dbTransactions.findByIdAndDelete(req.params.id) 6 | console.log(req.params.id, "was deleted") 7 | next() 8 | } catch (err) { 9 | console.error(err) 10 | next() 11 | } 12 | }; 13 | 14 | module.exports = { 15 | post, 16 | }; 17 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "0.0.1", 4 | "description": "Praise the golden sun", 5 | "main": "server.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "body-parser": "^1.20.2", 13 | "cors": "^2.8.5", 14 | "express": "^4.18.3", 15 | "http": "^0.0.1-security", 16 | "mongoose": "^8.2.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /server/controllers/edit/deleteTransactionVault.controller.js: -------------------------------------------------------------------------------- 1 | const dbTransactionsVaults = require('../../models/transactionsVaults') 2 | 3 | const post = async (req, res, next) => { 4 | try { 5 | await dbTransactionsVaults.findByIdAndDelete(req.params.id) 6 | console.log(req.params.id, "was deleted") 7 | next() 8 | } catch (err) { 9 | console.error(err) 10 | next() 11 | } 12 | }; 13 | 14 | module.exports = { 15 | post, 16 | }; 17 | -------------------------------------------------------------------------------- /client/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App.jsx"; 4 | import './i18n/i18n'; 5 | import "./index.css"; 6 | 7 | // PRIME REACT 8 | import { PrimeReactProvider } from "primereact/api"; 9 | import "primeflex/primeflex.css"; 10 | import "primereact/resources/primereact.css"; 11 | import "primeicons/primeicons.css"; 12 | 13 | ReactDOM.createRoot(document.getElementById("root")).render( 14 | 15 | 16 | 17 | ); 18 | -------------------------------------------------------------------------------- /client/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Budget5S 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /server/controllers/accounts/accounts.controller.js: -------------------------------------------------------------------------------- 1 | const dbAccounts = require("../../models/accounts"); 2 | 3 | const get = async (req, res, next) => { 4 | const data = await dbAccounts.find() 5 | res.send({ 6 | data 7 | }) 8 | }; 9 | 10 | const post = async (req, res, next) => { 11 | try { 12 | await dbAccounts.create({ 13 | label: req.body.data.label, 14 | balance: req.body.data.balance, 15 | creationDate: Date.now(), 16 | assignable: req.body.assignable, 17 | }); 18 | res.redirect('/accounts/') 19 | } catch (err) { 20 | console.error(err); 21 | } 22 | 23 | }; 24 | 25 | module.exports = { 26 | get, 27 | post, 28 | }; 29 | -------------------------------------------------------------------------------- /server/controllers/edit/vaultCell.controller.js: -------------------------------------------------------------------------------- 1 | const dbMonthlyVaults = require("../../models/monthlyVaults"); 2 | 3 | const post = async (req, res, next) => { 4 | 5 | switch (req.body.field) { 6 | case "monthly.assigned": 7 | await dbMonthlyVaults.findByIdAndUpdate( 8 | req.body.id, 9 | { assigned: req.body.value }, 10 | { new: true } 11 | ); 12 | break; 13 | case "monthly.target": 14 | await dbMonthlyVaults.findByIdAndUpdate( 15 | req.body.id, 16 | {target: req.body.value}, 17 | {new: true} 18 | ); 19 | break; 20 | 21 | default: 22 | break; 23 | } 24 | }; 25 | 26 | module.exports = { 27 | post, 28 | }; 29 | -------------------------------------------------------------------------------- /server/controllers/edit/bucketCell.controller.js: -------------------------------------------------------------------------------- 1 | const dbMonthlyBuckets = require("../../models/monthlyBuckets"); 2 | 3 | const post = async (req, res, next) => { 4 | 5 | switch (req.body.field) { 6 | case "monthly.assigned": 7 | await dbMonthlyBuckets.findByIdAndUpdate( 8 | req.body.id, 9 | { assigned: req.body.value }, 10 | { new: true } 11 | ); 12 | break; 13 | case "monthly.target": 14 | await dbMonthlyBuckets.findByIdAndUpdate( 15 | req.body.id, 16 | {target: req.body.value}, 17 | {new: true} 18 | ); 19 | break; 20 | 21 | default: 22 | break; 23 | } 24 | }; 25 | 26 | module.exports = { 27 | post, 28 | }; 29 | -------------------------------------------------------------------------------- /server/models/monthlyVaults.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const monthlyVaults = new mongoose.Schema({ 4 | vaultRef: { 5 | type: mongoose.Schema.Types.ObjectId, 6 | ref: "vaults", 7 | required: true, 8 | }, 9 | assigned: { type: Number, required: true }, 10 | month: { type: String, required: false }, 11 | year: { type: String, required: false }, 12 | date: { type: Date, required: true }, 13 | }); 14 | 15 | monthlyVaults.pre("save", function (next) { 16 | if (this.isNew) { 17 | const date = this.date; 18 | this.month = date.getMonth() + 1; 19 | this.year = date.getFullYear(); 20 | } 21 | next(); 22 | }); 23 | 24 | module.exports = mongoose.model("monthlyvaults", monthlyVaults); 25 | -------------------------------------------------------------------------------- /server/controllers/accounts/editAccount.controller.js: -------------------------------------------------------------------------------- 1 | const dbAccounts = require("../../models/accounts"); 2 | 3 | const get = async (req, res, next) => { 4 | try { 5 | const data = await dbAccounts.findById(req.params.id); 6 | res.send({ 7 | data, 8 | }); 9 | } catch (err) { 10 | console.error(err); 11 | } 12 | }; 13 | 14 | const post = async (req, res, next) => { 15 | const body = req.body; 16 | try { 17 | await dbAccounts.findOneAndUpdate( 18 | { _id: req.params.id }, 19 | { 20 | label: body.data.label, 21 | balance: body.data.balance, 22 | assignable: body.assignable, 23 | } 24 | ); 25 | } catch (err) { 26 | console.error(err); 27 | } 28 | }; 29 | 30 | module.exports = { 31 | get, 32 | post, 33 | }; 34 | -------------------------------------------------------------------------------- /server/models/accounts.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const accounts = new mongoose.Schema({ 4 | label: { type: String, required: true }, 5 | balance: { type: Number, required: true }, 6 | clearBalance: { type: Number, required: false }, 7 | unClearBalance: { type: Number, required: false }, 8 | currency: { type: String, required: false }, 9 | creationDate: { type: Date, required: true }, 10 | transactionsLinked: [ 11 | { 12 | type: mongoose.Schema.Types.ObjectId, 13 | ref: "transactions", 14 | }, 15 | ], 16 | color: { type: String, required: false }, // FOR THE STYLE 17 | icon: { type: String, required: false }, // FOR THE STYLE 18 | assignable: {type: Boolean, required: false, default: true} 19 | }); 20 | 21 | module.exports = mongoose.model("accounts", accounts); 22 | -------------------------------------------------------------------------------- /server/models/vaults.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const vaults = new mongoose.Schema({ 4 | label: { type: String, required: true }, 5 | accountLinked: { type: mongoose.Schema.Types.ObjectId, ref: "accounts" }, 6 | monthlyType: { type: Boolean, required: true }, 7 | dateLimit: { type: Date, required: false }, 8 | UNIXLimit: { 9 | type: Number, 10 | required: false, 11 | default: 253636783200000 /* = 999/99/99 */, 12 | }, 13 | dateDue: { type: Date, required: false }, 14 | dayDue: { type: Number, required: false }, 15 | amount: { type: Number, required: false }, 16 | balance: { type: Number, required: true }, 17 | target: { type: Number, required: false }, 18 | archived: {type: Boolean, required: true}, 19 | }); 20 | 21 | 22 | module.exports = mongoose.model("vaults", vaults); 23 | -------------------------------------------------------------------------------- /server/models/monthlyBuckets.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const monthlyBuckets = new mongoose.Schema({ 4 | bucketRef: { 5 | type: mongoose.Schema.Types.ObjectId, 6 | ref: "buckets", 7 | required: true, 8 | }, 9 | assigned: { type: Number, required: true }, 10 | month: { type: String, required: false }, 11 | year: { type: String, required: false }, 12 | date: { type: Date, required: true }, 13 | UNIXDate: {type: Number, required: true}, 14 | target: {type: Number, required: false}, 15 | }); 16 | 17 | monthlyBuckets.pre("save", function (next) { 18 | if (this.isNew) { 19 | const date = this.date; 20 | this.month = date.getMonth() + 1; 21 | this.year = date.getFullYear(); 22 | } 23 | next(); 24 | }); 25 | 26 | module.exports = mongoose.model("monthlybuckets", monthlyBuckets); -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | // IMPORT 2 | const mongoose = require("mongoose").mongoose.connect( 3 | "mongodb://mongo:27017/budget5s" 4 | ); 5 | const cors = require("cors"); 6 | const express = require("express"); 7 | const app = express(); 8 | const http = require("http").createServer(app); 9 | const bodyParser = require("body-parser"); 10 | 11 | // ROUTES 12 | const financeRoutes = require("./routes/finance"); 13 | const editDataRoutes = require("./routes/edit"); 14 | 15 | // CRUD 16 | app.use(bodyParser.json()); 17 | app.use( 18 | cors({ 19 | origin: "*", 20 | methods: ["GET", "POST"], 21 | allowedHeaders: ["Content-Type", "Authorization"], 22 | maxAge: 600, 23 | }) 24 | ); 25 | 26 | // ROUTES CALL 27 | app.use(financeRoutes, editDataRoutes); 28 | 29 | // 🚀 LAUNCH 30 | http.listen(3000, () => { 31 | console.log("🚀 Started on 3000"); 32 | }); 33 | -------------------------------------------------------------------------------- /server/models/expenses.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const expenses = new mongoose.Schema({ 4 | label: { type: String, required: false }, 5 | date: { type: Date, required: true }, 6 | month: { type: String, required: false }, 7 | year: { type: String, required: false }, 8 | amount: { type: Number, required: true }, 9 | bucketRef: { type: mongoose.Schema.Types.ObjectId, ref: "buckets" }, 10 | accountRef: { type: mongoose.Schema.Types.ObjectId, ref: "accounts" }, 11 | UNIXDate: { type: Number, required: false }, 12 | }); 13 | 14 | expenses.pre("save", function (next) { 15 | if (this.isNew) { 16 | const date = this.date; 17 | this.month = date.getMonth() + 1; 18 | this.year = date.getFullYear(); 19 | this.UNIXDate = new Date(date.getFullYear(), date.getMonth(), "1"); 20 | } 21 | next(); 22 | }); 23 | 24 | module.exports = mongoose.model("expenses", expenses); 25 | -------------------------------------------------------------------------------- /client/src/i18n/i18n.js: -------------------------------------------------------------------------------- 1 | import i18n from "i18next"; 2 | import { initReactI18next } from "react-i18next"; 3 | 4 | import translationEN from './translationEN.json' 5 | import translationFR from './translationFR.json' 6 | import translationES from './translationES.json' 7 | import translationIT from './translationIT.json' 8 | import translationDE from './translationDE.json' 9 | 10 | const resources = { 11 | en: { 12 | translation: translationEN 13 | }, 14 | fr: { 15 | translation: translationFR 16 | }, 17 | es: { 18 | translation: translationES 19 | }, 20 | it: { 21 | translation: translationIT 22 | }, 23 | de: { 24 | translation: translationDE 25 | } 26 | }; 27 | 28 | i18n 29 | .use(initReactI18next) 30 | .init({ 31 | resources, 32 | lng: "en", 33 | fallbackLng: "en", 34 | 35 | interpolation: { 36 | escapeValue: false 37 | } 38 | }); 39 | 40 | export default i18n; -------------------------------------------------------------------------------- /server/controllers/vaults/createVault.controller.js: -------------------------------------------------------------------------------- 1 | const dbAccounts = require("../../models/accounts"); 2 | const dbVaults = require("../../models/vaults"); 3 | 4 | const get = async (req, res, next) => { 5 | const dataAccounts = await dbAccounts.find(); 6 | res.send({ 7 | dataAccounts, 8 | }); 9 | }; 10 | 11 | const post = async (req, res, next) => { 12 | try { 13 | await dbVaults.create({ 14 | label: req.body.label, 15 | balance: 0, 16 | UNIXLimit: 253636783200000, 17 | dateDue: req.body.dateDue, 18 | creationDate: Date.now(), 19 | archived: false, 20 | accountLinked: req.body.accountLinked, 21 | monthlyType: req.body.monthlyType, 22 | dateDue: req.body.dateDue, 23 | target: req.body.target, 24 | }); 25 | } catch (err) { 26 | console.error(err); 27 | } 28 | }; 29 | 30 | module.exports = { 31 | get, 32 | post, 33 | }; 34 | -------------------------------------------------------------------------------- /server/models/transactionsVaults.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const transactionsVaults = new mongoose.Schema({ 4 | label: { type: String, required: false }, 5 | date: { type: Date, required: true }, 6 | month: { type: String, required: false }, 7 | year: { type: String, required: false }, 8 | UNIXDate: { type: Number, required: false }, 9 | amount: { type: Number, required: true }, 10 | vaultRef: { type: mongoose.Schema.Types.ObjectId, ref: "buckets" }, 11 | accountRef: { type: mongoose.Schema.Types.ObjectId, ref: "accounts" }, 12 | }); 13 | 14 | transactionsVaults.pre("save", function (next) { 15 | if (this.isNew) { 16 | const date = this.date; 17 | this.month = date.getMonth() + 1; 18 | this.year = date.getFullYear(); 19 | this.UNIXDate = new Date(this.year, this.month, "1"); 20 | } 21 | next(); 22 | }); 23 | 24 | module.exports = mongoose.model("transactionsvaults", transactionsVaults); 25 | -------------------------------------------------------------------------------- /server/models/buckets.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const buckets = new mongoose.Schema({ 4 | label: { type: String, required: true }, 5 | amount: { type: Number, default: 0, required: true }, 6 | balance: { type: Number, required: false }, 7 | defaultTarget: { type: Number, default: 0, required: true }, 8 | creationDate: { type: Date, required: true }, 9 | targeted: { type: Boolean, default: 0, required: false }, 10 | dayDue: { type: String, required: false }, 11 | type: { type: String, required: false }, // 12 | category: { type: String, required: false }, // 13 | dateLimit: { type: Date, required: false }, 14 | UNIXLimit: { 15 | type: Number, 16 | required: true, 17 | default: 253636783200000 /* = 999/99/99 */, 18 | }, 19 | note: { type: String, required: false }, // 20 | color: { type: String, required: false }, // 21 | icon: { type: String, required: false }, // 22 | }); 23 | 24 | 25 | module.exports = mongoose.model("buckets", buckets); 26 | -------------------------------------------------------------------------------- /server/controllers/buckets/editBucket.controller.js: -------------------------------------------------------------------------------- 1 | const dbBuckets = require("../../models/buckets"); 2 | 3 | const get = async (req, res, next) => { 4 | try { 5 | const findBucket = await dbBuckets.findById(req.params.id).lean(); 6 | res.send({ 7 | data: findBucket, 8 | }); 9 | } catch (err) { 10 | console.error(err); 11 | } 12 | } 13 | 14 | const post = async (req, res, next) => { 15 | const body = req.body 16 | const date = new Date(req.body.date) 17 | const unixDate = date.getTime() 18 | await dbBuckets.findOneAndUpdate({_id: body.data._id}, { 19 | label: body.data.label, 20 | type: body.data.type, 21 | category: body.data.category, 22 | color: body.data.color, 23 | monthly: body.data.monthly, 24 | amount: body.data.amount, 25 | balance: body.data.balance, 26 | dateLimit: body.date, 27 | UNIXLimit: unixDate, 28 | targeted: body.data.targeted 29 | 30 | }) 31 | } 32 | 33 | 34 | module.exports = { 35 | get, 36 | post, 37 | }; 38 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite --host", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@tabler/icons-react": "^3.1.0", 14 | "i18next": "^23.13.0", 15 | "primeflex": "^3.3.1", 16 | "primeicons": "^6.0.1", 17 | "primereact": "^10.5.1", 18 | "react": "^18.2.0", 19 | "react-dom": "^18.2.0", 20 | "react-i18next": "^15.0.1", 21 | "react-router-dom": "^6.22.3" 22 | }, 23 | "devDependencies": { 24 | "@types/react": "^18.2.64", 25 | "@types/react-dom": "^18.2.21", 26 | "@vitejs/plugin-react": "^4.2.1", 27 | "eslint": "^8.57.0", 28 | "eslint-plugin-react": "^7.34.0", 29 | "eslint-plugin-react-hooks": "^4.6.0", 30 | "eslint-plugin-react-refresh": "^0.4.5", 31 | "vite": "^5.1.6" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /server/models/transactions.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const transactions = new mongoose.Schema({ 4 | accountRef: { type: mongoose.Schema.Types.ObjectId, ref: "accounts" }, 5 | label: { type: String, required: false }, 6 | amount: { type: Number, required: true }, 7 | currency: { type: String, required: false }, 8 | date: { type: Date, required: true }, 9 | month: { type: String, required: false }, 10 | year: { type: String, required: false }, 11 | UNIXDate: { type: Number, required: false }, 12 | color: { type: String, required: false }, // FOR THE STYLE 13 | icon: { type: String, required: false }, // FOR THE STYLE 14 | }); 15 | 16 | transactions.pre("save", function (next) { 17 | if (this.isNew) { 18 | const date = this.date; 19 | this.month = date.getMonth() + 1; 20 | this.year = date.getFullYear(); 21 | this.UNIXDate = new Date(this.year, this.month, "1"); 22 | } 23 | next(); 24 | }); 25 | 26 | module.exports = mongoose.model("transactions", transactions); 27 | -------------------------------------------------------------------------------- /server/controllers/vaults/editVault.controller.js: -------------------------------------------------------------------------------- 1 | const dbVaults = require("../../models/vaults"); 2 | const dbAccounts = require("../../models/accounts"); 3 | 4 | const get = async (req, res, next) => { 5 | try { 6 | const dataVaults = await dbVaults.findById(req.params.id); 7 | const dataAccounts = await dbAccounts.findById(dataVaults.accountLinked); 8 | const listAccounts = await dbAccounts.find() 9 | res.send({ 10 | dataVaults, 11 | dataAccounts, 12 | listAccounts, 13 | }); 14 | } catch (err) { 15 | console.error(err); 16 | } 17 | } 18 | 19 | const post = async (req, res, next) => { 20 | const body = req.body.data 21 | const date = new Date(body.date) 22 | const unixDate = date.getTime() 23 | await dbVaults.findOneAndUpdate({_id: req.params.id}, { 24 | label: body.label, 25 | accountLinked: body.accountLinked, 26 | monthlyType: body.monthly, 27 | dateDue: date, 28 | UNIXLimit: 253636783200000, 29 | }) 30 | } 31 | 32 | 33 | module.exports = { 34 | get, 35 | post, 36 | }; -------------------------------------------------------------------------------- /client/src/Footer.jsx: -------------------------------------------------------------------------------- 1 | import { useTranslation } from 'react-i18next'; 2 | import { Dropdown } from 'primereact/dropdown'; 3 | 4 | function Footer() { 5 | const {t, i18n} = useTranslation() 6 | const languages = [ 7 | { name: 'English', code: 'en' }, 8 | { name: 'Français', code: 'fr' }, 9 | { name: 'Italiano', code: 'it' }, 10 | { name: 'Español', code: 'es' }, 11 | { name: 'German', code: 'de' } 12 | ]; 13 | 14 | const changeLanguage = (e) => { 15 | i18n.changeLanguage(e.value.code); 16 | }; 17 | return ( 18 | <> 19 |
20 |
21 |
22 | {t('Made to help anybody')} 23 | lang.code === i18n.language)} 25 | options={languages} 26 | onChange={changeLanguage} 27 | optionLabel="name" 28 | placeholder="Select a Language" 29 | className="p-mb-3 max-h-3rem flex align-items-center ml-6 justify-content-center" 30 | /> 31 |
32 | 33 |
34 |
35 | 36 | ); 37 | } 38 | 39 | export default Footer; 40 | -------------------------------------------------------------------------------- /client/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | listen [::]:80; 4 | server_name localhost; 5 | 6 | #access_log /var/log/nginx/host.access.log main; 7 | 8 | location / { 9 | root /usr/share/nginx/html; 10 | try_files $uri $uri/ /index.html; 11 | index index.html index.htm; 12 | } 13 | 14 | #error_page 404 /404.html; 15 | 16 | # redirect server error pages to the static page /50x.html 17 | # 18 | error_page 500 502 503 504 /50x.html; 19 | location = /50x.html { 20 | root /usr/share/nginx/html; 21 | } 22 | 23 | # proxy the PHP scripts to Apache listening on 127.0.0.1:80 24 | # 25 | #location ~ \.php$ { 26 | # proxy_pass http://127.0.0.1; 27 | #} 28 | 29 | # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 30 | # 31 | #location ~ \.php$ { 32 | # root html; 33 | # fastcgi_pass 127.0.0.1:9000; 34 | # fastcgi_index index.php; 35 | # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; 36 | # include fastcgi_params; 37 | #} 38 | 39 | # deny access to .htaccess files, if Apache's document root 40 | # concurs with nginx's one 41 | # 42 | #location ~ /\.ht { 43 | # deny all; 44 | #} 45 | } 46 | 47 | -------------------------------------------------------------------------------- /server/controllers/expense/addExpense.controller.js: -------------------------------------------------------------------------------- 1 | const dbAccounts = require("../../models/accounts"); 2 | const dbBuckets = require("../../models/buckets"); 3 | const dbTransactions = require("../../models/transactions"); 4 | const dbExpenses = require("../../models/expenses"); 5 | 6 | const get = async (req, res, next) => { 7 | const dataAccounts = await dbAccounts.find(); 8 | const dataBuckets = await dbBuckets.find(); 9 | res.send({ 10 | dataAccounts, 11 | dataBuckets, 12 | }); 13 | }; 14 | 15 | const post = async (req, res, next) => { 16 | try { 17 | const account = await dbAccounts.findById(req.body.account); 18 | await dbAccounts.findByIdAndUpdate( 19 | req.body.account, 20 | { balance: account.balance - req.body.amount }, 21 | { new: true } 22 | ); 23 | await dbExpenses.create({ 24 | label: req.body.label, 25 | amount: req.body.amount, 26 | date: req.body.date, 27 | bucketRef: req.body.bucket, 28 | accountRef: req.body.account 29 | }); 30 | const bucket = await dbBuckets.findById(req.body.bucket) 31 | await dbBuckets.findByIdAndUpdate(req.body.bucket, 32 | {balance : bucket.balance - req.body.amount}, 33 | {new : true} 34 | ) 35 | } catch (err) { 36 | console.error(err); 37 | } 38 | }; 39 | 40 | module.exports = { 41 | get, 42 | post, 43 | }; 44 | -------------------------------------------------------------------------------- /server/controllers/transactions/transactions.controller.js: -------------------------------------------------------------------------------- 1 | const dbAccounts = require("../../models/accounts"); 2 | const dbBuckets = require("../../models/buckets"); 3 | const dbTransactions = require("../../models/transactions") 4 | 5 | const get = async (req, res, next) => { 6 | const dataAccounts = await dbAccounts.find(); 7 | res.send({ 8 | dataAccounts, 9 | }); 10 | }; 11 | 12 | const post = async (req, res, next) => { 13 | if (req.body.options === true) { 14 | const newBalance = req.body.oldBalance + req.body.amount; 15 | await dbAccounts.findByIdAndUpdate( 16 | req.body.account, 17 | { balance: newBalance }, 18 | { new: true } 19 | ); 20 | await dbTransactions.create({ 21 | label: req.body.label, 22 | amount: req.body.amount, 23 | date: req.body.date, 24 | accountRef: req.body.account, 25 | }); 26 | } else { 27 | const newBalance = req.body.oldBalance - req.body.amount; 28 | await dbAccounts.findByIdAndUpdate( 29 | req.body.account, 30 | { balance: newBalance }, 31 | { new: true } 32 | ); 33 | await dbTransactions.create({ 34 | label: req.body.label, 35 | amount: -req.body.amount, 36 | date: req.body.date, 37 | accountRef: req.body.account, 38 | }); 39 | } 40 | }; 41 | 42 | module.exports = { 43 | get, 44 | post, 45 | }; 46 | -------------------------------------------------------------------------------- /client/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/controllers/transactions/transactionVault.controller.js: -------------------------------------------------------------------------------- 1 | const dbAccounts = require("../../models/accounts"); 2 | const dbVaults = require("../../models/vaults"); 3 | const dbTransactions = require("../../models/transactions"); 4 | const dbTransactionsVault = require("../../models/transactionsVaults"); 5 | 6 | const get = async (req, res, next) => { 7 | const dataAccounts = await dbAccounts.find(); 8 | const dataVaults = await dbVaults.find(); 9 | res.send({ 10 | dataAccounts, 11 | dataVaults, 12 | }); 13 | }; 14 | 15 | const post = async (req, res, next) => { 16 | try { 17 | // Update the source account 18 | await dbAccounts.findByIdAndUpdate( 19 | req.body.sourceAccount, 20 | { $inc: { balance: -req.body.amount } }, 21 | { new: true } 22 | ); 23 | // Update the linked account to the vault 24 | await dbAccounts.findByIdAndUpdate( 25 | req.body.accountLinked, 26 | { $inc: { balance: req.body.amount } }, 27 | { new: true } 28 | ); 29 | // Update the balance of the vault for non-monhtly type 30 | await dbVaults.findByIdAndUpdate(req.body.vault, { 31 | $inc: { balance: req.body.amount }, 32 | }); 33 | await dbTransactionsVault.create({ 34 | label: req.body.label, 35 | amount: req.body.amount, 36 | date: req.body.date, 37 | vaultRef: req.body.vault, 38 | accountRef: req.body.accountLinked, 39 | }); 40 | } catch (err) { 41 | console.error(err); 42 | } 43 | }; 44 | 45 | module.exports = { 46 | get, 47 | post, 48 | }; 49 | -------------------------------------------------------------------------------- /server/routes/edit.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | const editBucket = require("../controllers/buckets/editBucket.controller"); 5 | const editVault = require("../controllers/vaults/editVault.controller"); 6 | const editAccount = require("../controllers/accounts/editAccount.controller") 7 | 8 | 9 | const bucketCell = require("../controllers/edit/bucketCell.controller") 10 | const vaultCell = require("../controllers/edit/vaultCell.controller") 11 | 12 | const deleteBucket = require("../controllers/edit/deleteBucket.controller") 13 | const deleteVault = require("../controllers/edit/deleteVault.controller") 14 | const deleteAccount = require("../controllers/edit/deleteAccount.controller") 15 | const deleteTransaction = require("../controllers/edit/deleteTransaction.controller") 16 | const deleteTransactionVault = require("../controllers/edit/deleteTransactionVault.controller") 17 | const deleteExpense = require("../controllers/edit/deleteExpense.controller") 18 | 19 | router.post("/edit/bucketcell", bucketCell.post); 20 | 21 | router.post("/edit/vaultcell", vaultCell.post); 22 | 23 | router.get("/edit/bucket/:id/", editBucket.get); 24 | router.post("/edit/bucket/:id/", editBucket.post); 25 | 26 | 27 | router.get("/edit/vault/:id/", editVault.get); 28 | router.post("/edit/vault/:id/", editVault.post); 29 | 30 | router.get("/edit/account/:id/", editAccount.get); 31 | router.post("/edit/account/:id/", editAccount.post); 32 | 33 | 34 | // DELETE 35 | 36 | router.post("/edit/bucket/delete/:id/", deleteBucket.post) 37 | router.post("/edit/vault/delete/:id/", deleteVault.post) 38 | router.post("/edit/account/delete/:id/", deleteAccount.post) 39 | router.post("/edit/transaction/delete/:id/", deleteTransaction.post) 40 | router.post("/edit/transaction/vault/delete/:id/", deleteTransactionVault.post) 41 | router.post("/edit/expense/delete/:id/", deleteExpense.post) 42 | 43 | module.exports = router; 44 | -------------------------------------------------------------------------------- /client/src/finances/components/ResumeAccounts.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | import { useTranslation } from 'react-i18next'; 4 | 5 | const ResumeAccounts = () => { 6 | const {t, i18n} = useTranslation() 7 | 8 | const urlAPI = `${import.meta.env.VITE_BACKEND_ADRESS}:${import.meta.env.VITE_BACKEND_PORT}/all/accounts`; 9 | const [sumBalance, setSumBalance] = useState(0) 10 | const [balanceAssignable, setSumBalanceAssignable] = useState(0) 11 | const [balanceUnassignable, setSumBalanceUnassignable] = useState(0) 12 | const [data, setData] = useState([]); 13 | 14 | 15 | 16 | const fetchData = async () => { 17 | try { 18 | const fetchGet = await fetch(urlAPI), 19 | fetchResponse = await fetchGet.json(); 20 | return fetchResponse.data; 21 | } catch (error) { 22 | console.error("An error occurred while executing fetchData() : ", error); 23 | } 24 | }; 25 | 26 | const showAccounts = data.map((account) => ( 27 |
31 |
32 | {account.label} 33 |
{account.balance} €
34 |
35 |
36 | )); 37 | 38 | useEffect(() => { 39 | 40 | fetchData().then((data) => { 41 | setData(data); 42 | setSumBalance(() => data.reduce((acc, curr) => acc + curr.balance, 0)) 43 | setSumBalanceAssignable(() => data.filter(object => object.assignable).reduce((acc, curr) => acc + curr.balance, 0)) 44 | setSumBalanceUnassignable(() => data.filter(object => !object.assignable).reduce((acc, curr) => acc + curr.balance, 0)) 45 | 46 | }); 47 | 48 | console.log(balanceAssignable) 49 | }, []); 50 | 51 | return ( 52 | <> 53 |
{showAccounts}
54 | 55 |
56 |
57 |
{t('Total on your account')} : {sumBalance} €
58 |
{t('Total unassignable')} : -{balanceUnassignable} €
59 |
{t('Total assignable')} : {balanceAssignable} €
60 | 61 |
62 |
63 | 64 | ); 65 | }; 66 | 67 | export default ResumeAccounts; 68 | -------------------------------------------------------------------------------- /server/controllers/vaults/vault.controller.js: -------------------------------------------------------------------------------- 1 | const dbVault = require("../../models/vaults"); 2 | const dbMonthlyVaults = require("../../models/monthlyVaults"); 3 | const dbTransactionsVaults = require("../../models/transactionsVaults"); 4 | 5 | const get = async (req, res, next) => { 6 | try { 7 | const month = req.params.month, 8 | formatedMonth = month < 10 ? `0${month}` : `${month}`; 9 | const dateFilter = new Date(`${req.params.year}-${formatedMonth}`); 10 | 11 | const findAllVault = await dbVault 12 | .find({ UNIXLimit: { $gt: (dateFilter.getTime()) }, archived: false}) 13 | .lean(); 14 | 15 | async function monthly(idVault, date) { 16 | const checkData = await dbMonthlyVaults 17 | .findOne({ vaultRef: idVault, date: date }) 18 | .lean(); 19 | 20 | if (checkData !== null) { 21 | return checkData; 22 | } else { 23 | await dbMonthlyVaults.create({ 24 | vaultRef: idVault, 25 | assigned: 0, 26 | date: dateFilter, 27 | month: req.params.month, 28 | year: req.params.year, 29 | }); 30 | return await dbMonthlyVaults 31 | .findOne({ vaultRef: idVault, date: date }) 32 | .lean(); 33 | } 34 | } 35 | 36 | async function transactionsMonthly(idVault, month, year) { 37 | const findTransactionsVault = await dbTransactionsVaults.find({ 38 | vaultRef: idVault, 39 | month: month, 40 | year: year, 41 | }); 42 | return findTransactionsVault; 43 | } 44 | 45 | 46 | async function transactionsVault(idVault) { 47 | const findTransactionsVault = await dbTransactionsVaults.find({ 48 | vaultRef: idVault, 49 | }); 50 | return findTransactionsVault; 51 | } 52 | 53 | async function prevTransactions(idVault) { 54 | const findPrevTrans = await dbTransactionsVaults.find({ 55 | vaultRef: idVault, 56 | UNIXDate: { $lt: dateFilter.getTime() }, 57 | }); 58 | return findPrevTrans; 59 | } 60 | 61 | const mergedVault = await Promise.all( 62 | findAllVault.map(async (prevData) => ({ 63 | ...prevData, 64 | monthly: await monthly(prevData._id, dateFilter), 65 | transactions: (await transactionsMonthly(prevData._id, req.params.month, req.params.year) 66 | ).reduce((total, item) => total + item.amount, 0), 67 | prevTransactions: ( 68 | await prevTransactions(prevData._id) 69 | ).reduce((total, item) => total + item.amount, 0), 70 | })) 71 | ); 72 | 73 | 74 | res.send({ 75 | data: mergedVault, 76 | }); 77 | } catch (err) { 78 | console.error(err); 79 | } 80 | }; 81 | 82 | const post = async (req, res, next) => {}; 83 | 84 | module.exports = { 85 | get, 86 | post, 87 | }; 88 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /public/logo.svg 2 | .DS_Store 3 | 4 | /client/soho-dark.css 5 | /client/soho-light.css 6 | 7 | # Logs 8 | logs 9 | *.log 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | lerna-debug.log* 14 | .pnpm-debug.log* 15 | 16 | # Diagnostic reports (https://nodejs.org/api/report.html) 17 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 18 | 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | *.pid.lock 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | *.lcov 31 | 32 | # nyc test coverage 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 36 | .grunt 37 | 38 | # Bower dependency directory (https://bower.io/) 39 | bower_components 40 | 41 | # node-waf configuration 42 | .lock-wscript 43 | 44 | # Compiled binary addons (https://nodejs.org/api/addons.html) 45 | build/Release 46 | 47 | # Dependency directories 48 | node_modules/ 49 | jspm_packages/ 50 | 51 | # Snowpack dependency directory (https://snowpack.dev/) 52 | web_modules/ 53 | 54 | # TypeScript cache 55 | *.tsbuildinfo 56 | 57 | # Optional npm cache directory 58 | .npm 59 | 60 | # Optional eslint cache 61 | .eslintcache 62 | 63 | # Optional stylelint cache 64 | .stylelintcache 65 | 66 | # Microbundle cache 67 | .rpt2_cache/ 68 | .rts2_cache_cjs/ 69 | .rts2_cache_es/ 70 | .rts2_cache_umd/ 71 | 72 | # Optional REPL history 73 | .node_repl_history 74 | 75 | # Output of 'npm pack' 76 | *.tgz 77 | 78 | # Yarn Integrity file 79 | .yarn-integrity 80 | 81 | # dotenv environment variable files 82 | # .env 83 | # .env.development.local 84 | # .env.test.local 85 | # .env.production.local 86 | # .env.local 87 | 88 | # parcel-bundler cache (https://parceljs.org/) 89 | .cache 90 | .parcel-cache 91 | 92 | # Next.js build output 93 | .next 94 | out 95 | 96 | # Nuxt.js build / generate output 97 | .nuxt 98 | dist 99 | 100 | # Gatsby files 101 | .cache/ 102 | # Comment in the public line in if your project uses Gatsby and not Next.js 103 | # https://nextjs.org/blog/next-9-1#public-directory-support 104 | # public 105 | 106 | # vuepress build output 107 | .vuepress/dist 108 | 109 | # vuepress v2.x temp and cache directory 110 | .temp 111 | .cache 112 | 113 | # Docusaurus cache and generated files 114 | .docusaurus 115 | 116 | # Serverless directories 117 | .serverless/ 118 | 119 | # FuseBox cache 120 | .fusebox/ 121 | 122 | # DynamoDB Local files 123 | .dynamodb/ 124 | 125 | # TernJS port file 126 | .tern-port 127 | 128 | # Stores VSCode versions used for testing VSCode extensions 129 | .vscode-test 130 | 131 | # yarn v2 132 | .yarn/cache 133 | .yarn/unplugged 134 | .yarn/build-state.yml 135 | .yarn/install-state.gz 136 | .pnp.* -------------------------------------------------------------------------------- /client/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { Route, Routes, BrowserRouter } from "react-router-dom"; 2 | 3 | // ACCOUNTS 4 | import Accounts from "./finances/accounts/Accounts"; 5 | import AddAccounts from "./finances/accounts/AddAccounts"; 6 | import EditAccount from "./finances/accounts/EditAccount"; 7 | // BUCKETS 8 | import Buckets from "./finances/buckets/Buckets"; 9 | import CreateBucket from "./finances/buckets/CreateBucket"; 10 | import ListBuckets from "./finances/buckets/ListBucket"; 11 | import EditBucket from "./finances/buckets/EditBucket"; 12 | // VAULTS 13 | import Vaults from "./finances/vaults/Vaults"; 14 | import CreateVault from "./finances/vaults/CreateVault"; 15 | import ListVaults from "./finances/vaults/ListVault"; 16 | import EditVault from "./finances/vaults/EditVault"; 17 | // TRANSACTIONS 18 | import AddTransactions from "./finances/transactions/AddTransactions"; 19 | import TransactionsVault from "./finances/transactions/TransactionsVault"; 20 | import TransactionsVaultList from "./finances/vaults/ListVaultTransactions"; 21 | import ListTransactions from "./finances/transactions/ListTransaction"; 22 | // EXPENSES 23 | import AddExpense from "./finances/expense/AddExpense"; 24 | import ListExpenses from "./finances/expense/ListExpenses"; 25 | // MISC 26 | import Listing from "./finances/Listing"; 27 | import Layout from "./Layout"; 28 | import Footer from "./Footer"; 29 | 30 | function App() { 31 | return ( 32 | <> 33 | 34 | 35 | }> 36 | } /> 37 | } /> 38 | } /> 39 | 40 | } /> 41 | } /> 42 | 43 | } /> 44 | } /> 45 | } /> 46 | } /> 47 | } /> 48 | } /> 49 | } /> 50 | } /> 51 | } /> 52 | 53 | } /> 54 | } /> 55 | } /> 56 | } 59 | /> 60 | 61 | 62 |