diff --git a/package-lock.json b/package-lock.json index 653e4e0fae..0069c717a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,8 @@ "@fortawesome/fontawesome-free": "^6.4.0", "@popperjs/core": "^2.11.8", "alpinejs": "^3.12.3", - "apexcharts": "^3.41.0", "bootstrap": "^5.3.0", + "chart.js": "^4.3.3", "date-fns": "^2.30.0", "store": "^2.0.12" }, @@ -392,6 +392,11 @@ "node": ">=6" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -435,19 +440,6 @@ "node": ">= 8" } }, - "node_modules/apexcharts": { - "version": "3.41.1", - "resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.41.1.tgz", - "integrity": "sha512-kta8fhXrfZYqW7K9kF7FqZ6imQaC6moyRgcUZjwIky/oeHVVISSN/2rjUIvZXnwxWHiSdDHMqLy+TqJhB4DXFA==", - "dependencies": { - "svg.draggable.js": "^2.2.2", - "svg.easing.js": "^2.0.0", - "svg.filter.js": "^2.0.2", - "svg.pathmorphing.js": "^0.1.3", - "svg.resize.js": "^1.4.3", - "svg.select.js": "^3.0.1" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -504,6 +496,17 @@ "node": ">=8" } }, + "node_modules/chart.js": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.3.tgz", + "integrity": "sha512-aTk7pBw+x6sQYhon/NR3ikfUJuym/LdgpTlgZRe2PaEhjUMKBKyNaFCMVRAyTEWYFNO7qRu7iQVqOw/OqzxZxQ==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=7" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -907,89 +910,6 @@ "node": "*" } }, - "node_modules/svg.draggable.js": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz", - "integrity": "sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==", - "dependencies": { - "svg.js": "^2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.easing.js": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz", - "integrity": "sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==", - "dependencies": { - "svg.js": ">=2.3.x" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.filter.js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz", - "integrity": "sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==", - "dependencies": { - "svg.js": "^2.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.js": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz", - "integrity": "sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==" - }, - "node_modules/svg.pathmorphing.js": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz", - "integrity": "sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==", - "dependencies": { - "svg.js": "^2.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.resize.js": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz", - "integrity": "sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==", - "dependencies": { - "svg.js": "^2.6.5", - "svg.select.js": "^2.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.resize.js/node_modules/svg.select.js": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz", - "integrity": "sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==", - "dependencies": { - "svg.js": "^2.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/svg.select.js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz", - "integrity": "sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==", - "dependencies": { - "svg.js": "^2.6.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 6824e20c1c..075a651fc8 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "@fortawesome/fontawesome-free": "^6.4.0", "@popperjs/core": "^2.11.8", "alpinejs": "^3.12.3", - "apexcharts": "^3.41.0", "bootstrap": "^5.3.0", + "chart.js": "^4.3.3", "date-fns": "^2.30.0", "store": "^2.0.12" } diff --git a/resources/assets/v2/pages/dashboard/accounts.js b/resources/assets/v2/pages/dashboard/accounts.js index 923ef06f03..0f47135f01 100644 --- a/resources/assets/v2/pages/dashboard/accounts.js +++ b/resources/assets/v2/pages/dashboard/accounts.js @@ -18,7 +18,7 @@ * along with this program. If not, see . */ -import ApexCharts from "apexcharts"; +//import ApexCharts from "apexcharts"; import {getVariable} from "../../store/get-variable.js"; import {setVariable} from "../../store/set-variable.js"; import Dashboard from "../../api/v2/chart/account/dashboard.js"; @@ -142,8 +142,8 @@ export default () => ({ this.chart.updateOptions(this.chartOptions); } if (null === this.chart) { - this.chart = new ApexCharts(document.querySelector("#account-chart"), this.chartOptions); - this.chart.render(); + //this.chart = new ApexCharts(document.querySelector("#account-chart"), this.chartOptions); + //this.chart.render(); } }, loadAccounts() { @@ -211,13 +211,13 @@ export default () => ({ this.autoConversion = values[1]; // main dashboard chart: this.loadChart(); - this.loadAccounts(); + // this.loadAccounts(); }); window.store.observe('end', () => { this.chartData = null; this.expenseAccountChart = null; // main dashboard chart: - this.loadChart(); + // this.loadChart(); this.loadAccounts(); }); }, diff --git a/resources/assets/v2/pages/dashboard/budgets.js b/resources/assets/v2/pages/dashboard/budgets.js index ec532593de..3001d2d757 100644 --- a/resources/assets/v2/pages/dashboard/budgets.js +++ b/resources/assets/v2/pages/dashboard/budgets.js @@ -19,8 +19,9 @@ */ import {getVariable} from "../../store/get-variable.js"; import Dashboard from "../../api/v2/chart/budget/dashboard.js"; -import ApexCharts from "apexcharts"; -import formatMoney from "../../util/format-money.js"; +// todo optimize +import Chart from 'chart.js/auto'; +import {getDefaultChartSettings} from "../../support/default-chart-settings.js"; window.budgetCurrencies = []; export default () => ({ @@ -40,18 +41,20 @@ export default () => ({ if (null !== this.chartData) { this.generateOptions(this.chartData); this.drawChart(); + this.loading = false; } - - this.loading = false; }, drawChart() { if (null !== this.chart) { // chart already in place, refresh: - this.chart.updateOptions(this.chartOptions); + console.log('refresh'); + this.chart.data = this.chartOptions.data; + //this.chart.updateOptions(this.chartOptions); } if (null === this.chart) { - this.chart = new ApexCharts(document.querySelector("#budget-chart"), this.chartOptions); - this.chart.render(); + //this.chart = new ApexCharts(document.querySelector("#budget-chart"), this.chartOptions); + this.chart = new Chart(document.querySelector("#budget-chart"), this.chartOptions); + //this.chart.render(); } }, getFreshData() { @@ -60,128 +63,210 @@ export default () => ({ this.chartData = response.data; this.generateOptions(this.chartData); this.drawChart(); + this.loading = false; }); }, generateOptions(data) { window.budgetCurrencies = []; - let options = { - legend: {show: false}, - series: [{ - name: 'Spent', - data: [] - }, { - name: 'Left', - data: [] - }, { - name: 'Overspent', - data: [] - }], - chart: { - type: 'bar', - height: 400, - stacked: true, - toolbar: {tools: {zoom: false, download: false, pan: false}}, - zoom: { - enabled: true - } - }, - responsive: [{ - breakpoint: 480, - options: { - legend: { - position: 'bottom', - offsetX: -10, - offsetY: 0 + let options = getDefaultChartSettings('column'); + options.options.locale = window.store.get('locale').replace('_', '-'); + options.options.plugins = { + tooltip: { + callbacks: { + // tooltip: function (context) { + // //console.log(context); + // }, + title: function (context) { + return context.label; + }, + label: function (context) { + let label = context.dataset.label || ''; + + if (label) { + label += ': '; + } + //console.log('label'); + //console.log(context.label + ' X'); + //return context.label + ' X'; + // console.log(context); + return label + ' ' + context.parsed.x; + // return label + ' ' + formatMoney(context.parsed.y, window.budgetCurrencies[context.parsed.x] ?? 'EUR'); } } - }], - plotOptions: { - bar: { - horizontal: false, - borderRadius: 10, - dataLabels: { - total: { - enabled: true, - // style: { - // fontSize: '13px', - // fontWeight: 900 - // }, - formatter: function (val, opt) { - let index = 0; - if (typeof opt === 'object') { - index = opt.dataPointIndex; // this is the "category name + currency" index - } - let currencyCode = window.budgetCurrencies[index] ?? 'EUR'; - return formatMoney(val, currencyCode); - } - } - } - }, - }, - yaxis: { - labels: { - formatter: function (value, index) { - if (undefined === value) { - return value; - } - if (undefined === index) { - return value; - } - if (typeof index === 'object') { - index = index.dataPointIndex; // this is the "category name + currency" index - } - let currencyCode = window.budgetCurrencies[index] ?? 'EUR'; - return formatMoney(value, currencyCode); - } - } - }, - xaxis: { - categories: [] - }, - fill: { - opacity: 0.8 - }, - dataLabels: { - formatter: function (val, opt) { - let index = 0; - if (typeof opt === 'object') { - index = opt.dataPointIndex; // this is the "category name + currency" index - } - let currencyCode = window.budgetCurrencies[index] ?? 'EUR'; - return formatMoney(val, currencyCode); - }, } }; - + options.data = { + labels: [], + datasets: [ + { + label: 'TODO spent', + data: [], + borderWidth: 1, + stack: 1 + }, + { + label: 'TODO left', + data: [], + borderWidth: 1, + stack: 1 + }, + { + label: 'TODO overspent', + data: [], + borderWidth: 1, + stack: 1 + } + ] + }; for (const i in data) { if (data.hasOwnProperty(i)) { let current = data[i]; - // convert to EUR yes no? + // // convert to EUR yes no? let label = current.label + ' (' + current.currency_code + ')'; - options.xaxis.categories.push(label); + options.data.labels.push(label); if (this.autoConversion) { window.budgetCurrencies.push(current.native_code); - // series 0: spent - options.series[0].data.push(parseFloat(current.native_entries.spent) * -1); + options.data.datasets[0].data.push(parseFloat(current.native_entries.spent) * -1); // series 1: left - options.series[1].data.push(parseFloat(current.native_entries.left)); + options.data.datasets[1].data.push(parseFloat(current.native_entries.left)); // series 2: overspent - options.series[2].data.push(parseFloat(current.native_entries.overspent)); + options.data.datasets[2].data.push(parseFloat(current.native_entries.overspent)); } if (!this.autoConversion) { window.budgetCurrencies.push(current.currency_code); // series 0: spent - options.series[0].data.push(parseFloat(current.entries.spent) * -1); + options.data.datasets[0].data.push(parseFloat(current.entries.spent) * -1); // series 1: left - options.series[1].data.push(parseFloat(current.entries.left)); + options.data.datasets[1].data.push(parseFloat(current.entries.left)); // series 2: overspent - options.series[2].data.push(parseFloat(current.entries.overspent)); + options.data.datasets[2].data.push(parseFloat(current.entries.overspent)); } - + // console.log('Currencies'); + // console.log(window.budgetCurrencies); + // } } + + // options = { + // legend: {show: false}, + // series: [{ + // name: 'Spent', + // data: [] + // }, { + // name: 'Left', + // data: [] + // }, { + // name: 'Overspent', + // data: [] + // }], + // chart: { + // type: 'bar', + // height: 400, + // stacked: true, + // toolbar: {tools: {zoom: false, download: false, pan: false}}, + // zoom: { + // enabled: true + // } + // }, + // responsive: [{ + // breakpoint: 480, + // options: { + // legend: { + // position: 'bottom', + // offsetX: -10, + // offsetY: 0 + // } + // } + // }], + // plotOptions: { + // bar: { + // horizontal: false, + // borderRadius: 10, + // dataLabels: { + // total: { + // enabled: true, + // // style: { + // // fontSize: '13px', + // // fontWeight: 900 + // // }, + // formatter: function (val, opt) { + // let index = 0; + // if (typeof opt === 'object') { + // index = opt.dataPointIndex; // this is the "category name + currency" index + // } + // let currencyCode = window.budgetCurrencies[index] ?? 'EUR'; + // return formatMoney(val, currencyCode); + // } + // } + // } + // }, + // }, + // yaxis: { + // labels: { + // formatter: function (value, index) { + // if (undefined === value) { + // return value; + // } + // if (undefined === index) { + // return value; + // } + // if (typeof index === 'object') { + // index = index.dataPointIndex; // this is the "category name + currency" index + // } + // let currencyCode = window.budgetCurrencies[index] ?? 'EUR'; + // return formatMoney(value, currencyCode); + // } + // } + // }, + // xaxis: { + // categories: [] + // }, + // fill: { + // opacity: 0.8 + // }, + // dataLabels: { + // formatter: function (val, opt) { + // let index = 0; + // if (typeof opt === 'object') { + // index = opt.dataPointIndex; // this is the "category name + currency" index + // } + // let currencyCode = window.budgetCurrencies[index] ?? 'EUR'; + // return formatMoney(val, currencyCode); + // }, + // } + // }; + + + // for (const i in data) { + // if (data.hasOwnProperty(i)) { + // let current = data[i]; + // // convert to EUR yes no? + // let label = current.label + ' (' + current.currency_code + ')'; + // options.xaxis.categories.push(label); + // if (this.autoConversion) { + // window.budgetCurrencies.push(current.native_code); + // + // // series 0: spent + // options.series[0].data.push(parseFloat(current.native_entries.spent) * -1); + // // series 1: left + // options.series[1].data.push(parseFloat(current.native_entries.left)); + // // series 2: overspent + // options.series[2].data.push(parseFloat(current.native_entries.overspent)); + // } + // if (!this.autoConversion) { + // window.budgetCurrencies.push(current.currency_code); + // // series 0: spent + // options.series[0].data.push(parseFloat(current.entries.spent) * -1); + // // series 1: left + // options.series[1].data.push(parseFloat(current.entries.left)); + // // series 2: overspent + // options.series[2].data.push(parseFloat(current.entries.overspent)); + // } + // + // } + // } this.chartOptions = options; }, diff --git a/resources/assets/v2/pages/dashboard/categories.js b/resources/assets/v2/pages/dashboard/categories.js index 25d374e5a2..7cd71fb14e 100644 --- a/resources/assets/v2/pages/dashboard/categories.js +++ b/resources/assets/v2/pages/dashboard/categories.js @@ -19,7 +19,7 @@ */ import {getVariable} from "../../store/get-variable.js"; import Dashboard from "../../api/v2/chart/category/dashboard.js"; -import ApexCharts from "apexcharts"; +//import ApexCharts from "apexcharts"; import formatMoney from "../../util/format-money.js"; window.categoryCurrencies = []; @@ -203,16 +203,16 @@ export default () => ({ }, init() { Promise.all([getVariable('autoConversion', false),]).then((values) => { - this.autoConversion = values[0]; - this.loadChart(); + // this.autoConversion = values[0]; + // this.loadChart(); }); window.store.observe('end', () => { - this.chartData = null; - this.loadChart(); + // this.chartData = null; + // this.loadChart(); }); window.store.observe('autoConversion', (newValue) => { - this.autoConversion = newValue; - this.loadChart(); + // this.autoConversion = newValue; + // this.loadChart(); }); }, diff --git a/resources/assets/v2/support/default-chart-settings.js b/resources/assets/v2/support/default-chart-settings.js new file mode 100644 index 0000000000..c42ca77328 --- /dev/null +++ b/resources/assets/v2/support/default-chart-settings.js @@ -0,0 +1,38 @@ +/* + * default-chart-settings.js + * Copyright (c) 2023 james@firefly-iii.org + * + * This file is part of Firefly III (https://github.com/firefly-iii). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +function getDefaultChartSettings(type) { + if ('column' === type) { + return { + type: 'bar', + data: {}, + options: { + scales: { + y: { + beginAtZero: true + } + } + }, + }; + } + return []; +} + +export {getDefaultChartSettings}; diff --git a/resources/views/v2/index.blade.php b/resources/views/v2/index.blade.php index b47d7dcb1e..fc40ae2f11 100644 --- a/resources/views/v2/index.blade.php +++ b/resources/views/v2/index.blade.php @@ -51,7 +51,9 @@
-
+
+ +