Lots of new code.

This commit is contained in:
James Cole 2023-07-24 18:58:35 +02:00
parent f1173263b6
commit a6503fda39
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
31 changed files with 693 additions and 15692 deletions

174
package-lock.json generated
View File

@ -8,6 +8,7 @@
"@fortawesome/fontawesome-free": "^6.4.0",
"@popperjs/core": "^2.11.8",
"alpinejs": "^3.12.3",
"apexcharts": "^3.41.0",
"bootstrap": "^5.3.0",
"date-fns": "^2.30.0",
"store": "^2.0.12"
@ -434,6 +435,19 @@
"node": ">= 8"
}
},
"node_modules/apexcharts": {
"version": "3.41.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.41.0.tgz",
"integrity": "sha512-FJXA7NVjxs1q+ptR3b1I+pN8K/gWuXn+qLZjFz8EHvJOokdgcuwa/HSe5aC465HW/LWnrjWLSTsOQejQbQ42hQ==",
"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",
@ -893,6 +907,89 @@
"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",
@ -1178,6 +1275,19 @@
"picomatch": "^2.0.4"
}
},
"apexcharts": {
"version": "3.41.0",
"resolved": "https://registry.npmjs.org/apexcharts/-/apexcharts-3.41.0.tgz",
"integrity": "sha512-FJXA7NVjxs1q+ptR3b1I+pN8K/gWuXn+qLZjFz8EHvJOokdgcuwa/HSe5aC465HW/LWnrjWLSTsOQejQbQ42hQ==",
"requires": {
"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"
}
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -1474,6 +1584,70 @@
"resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz",
"integrity": "sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw=="
},
"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==",
"requires": {
"svg.js": "^2.0.1"
}
},
"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==",
"requires": {
"svg.js": ">=2.3.x"
}
},
"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==",
"requires": {
"svg.js": "^2.2.5"
}
},
"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=="
},
"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==",
"requires": {
"svg.js": "^2.4.0"
}
},
"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==",
"requires": {
"svg.js": "^2.6.5",
"svg.select.js": "^2.1.2"
},
"dependencies": {
"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==",
"requires": {
"svg.js": "^2.2.5"
}
}
}
},
"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==",
"requires": {
"svg.js": "^2.6.5"
}
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",

View File

@ -15,6 +15,7 @@
"@fortawesome/fontawesome-free": "^6.4.0",
"@popperjs/core": "^2.11.8",
"alpinejs": "^3.12.3",
"apexcharts": "^3.41.0",
"bootstrap": "^5.3.0",
"date-fns": "^2.30.0",
"store": "^2.0.12"

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -136,10 +136,10 @@
* Constants
* ------------------------------------------------------------------------
*/
const DATA_KEY$2 = 'lte.push-menu';
const EVENT_KEY$2 = `.${DATA_KEY$2}`;
const EVENT_OPEN = `open${EVENT_KEY$2}`;
const EVENT_COLLAPSE = `collapse${EVENT_KEY$2}`;
const DATA_KEY$1 = 'lte.push-menu';
const EVENT_KEY$1 = `.${DATA_KEY$1}`;
const EVENT_OPEN = `open${EVENT_KEY$1}`;
const EVENT_COLLAPSE = `collapse${EVENT_KEY$1}`;
const CLASS_NAME_SIDEBAR_MINI = 'sidebar-mini';
const CLASS_NAME_SIDEBAR_COLLAPSE = 'sidebar-collapse';
const CLASS_NAME_SIDEBAR_OPEN = 'sidebar-open';
@ -282,17 +282,17 @@
* ------------------------------------------------------------------------
*/
// const NAME = 'Treeview'
const DATA_KEY$1 = 'lte.treeview';
const EVENT_KEY$1 = `.${DATA_KEY$1}`;
const EVENT_EXPANDED$1 = `expanded${EVENT_KEY$1}`;
const EVENT_COLLAPSED$1 = `collapsed${EVENT_KEY$1}`;
const DATA_KEY = 'lte.treeview';
const EVENT_KEY = `.${DATA_KEY}`;
const EVENT_EXPANDED = `expanded${EVENT_KEY}`;
const EVENT_COLLAPSED = `collapsed${EVENT_KEY}`;
// const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`
const CLASS_NAME_MENU_OPEN = 'menu-open';
const SELECTOR_NAV_ITEM = '.nav-item';
const SELECTOR_NAV_LINK = '.nav-link';
const SELECTOR_TREEVIEW_MENU = '.nav-treeview';
const SELECTOR_DATA_TOGGLE = '[data-lte-toggle="treeview"]';
const Default$1 = {
const Default = {
animationSpeed: 300
};
/**
@ -302,11 +302,11 @@
class Treeview {
constructor(element, config) {
this._element = element;
this._config = Object.assign(Object.assign({}, Default$1), config);
this._config = Object.assign(Object.assign({}, Default), config);
}
open() {
var _a;
const event = new Event(EVENT_EXPANDED$1);
const event = new Event(EVENT_EXPANDED);
this._element.classList.add(CLASS_NAME_MENU_OPEN);
const childElement = (_a = this._element) === null || _a === void 0 ? void 0 : _a.querySelector(SELECTOR_TREEVIEW_MENU);
if (childElement) {
@ -316,7 +316,7 @@
}
close() {
var _a;
const event = new Event(EVENT_COLLAPSED$1);
const event = new Event(EVENT_COLLAPSED);
this._element.classList.remove(CLASS_NAME_MENU_OPEN);
const childElement = (_a = this._element) === null || _a === void 0 ? void 0 : _a.querySelector(SELECTOR_TREEVIEW_MENU);
if (childElement) {
@ -349,203 +349,13 @@
event.preventDefault();
}
if (targetItem) {
const data = new Treeview(targetItem, Default$1);
const data = new Treeview(targetItem, Default);
data.toggle();
}
});
});
});
/**
* --------------------------------------------
* AdminLTE card-widget.ts
* License MIT
* --------------------------------------------
*/
/**
* Constants
* ====================================================
*/
const DATA_KEY = 'lte.card-widget';
const EVENT_KEY = `.${DATA_KEY}`;
const EVENT_COLLAPSED = `collapsed${EVENT_KEY}`;
const EVENT_EXPANDED = `expanded${EVENT_KEY}`;
const EVENT_REMOVE = `remove${EVENT_KEY}`;
const EVENT_MAXIMIZED = `maximized${EVENT_KEY}`;
const EVENT_MINIMIZED = `minimized${EVENT_KEY}`;
const CLASS_NAME_CARD = 'card';
const CLASS_NAME_COLLAPSED = 'collapsed-card';
const CLASS_NAME_COLLAPSING = 'collapsing-card';
const CLASS_NAME_EXPANDING = 'expanding-card';
const CLASS_NAME_WAS_COLLAPSED = 'was-collapsed';
const CLASS_NAME_MAXIMIZED = 'maximized-card';
const SELECTOR_DATA_REMOVE = '[data-lte-toggle="card-remove"]';
const SELECTOR_DATA_COLLAPSE = '[data-lte-toggle="card-collapse"]';
const SELECTOR_DATA_MAXIMIZE = '[data-lte-toggle="card-maximize"]';
const SELECTOR_CARD = `.${CLASS_NAME_CARD}`;
const SELECTOR_CARD_BODY = '.card-body';
const SELECTOR_CARD_FOOTER = '.card-footer';
const Default = {
animationSpeed: 500,
collapseTrigger: SELECTOR_DATA_COLLAPSE,
removeTrigger: SELECTOR_DATA_REMOVE,
maximizeTrigger: SELECTOR_DATA_MAXIMIZE
};
class CardWidget {
constructor(element, config) {
this._element = element;
this._parent = element.closest(SELECTOR_CARD);
if (element.classList.contains(CLASS_NAME_CARD)) {
this._parent = element;
}
this._config = Object.assign(Object.assign({}, Default), config);
}
collapse() {
var _a, _b;
const event = new Event(EVENT_COLLAPSED);
if (this._parent) {
this._parent.classList.add(CLASS_NAME_COLLAPSING);
const elm = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`${SELECTOR_CARD_BODY}, ${SELECTOR_CARD_FOOTER}`);
elm.forEach(el => {
if (el instanceof HTMLElement) {
slideUp(el, this._config.animationSpeed);
}
});
setTimeout(() => {
if (this._parent) {
this._parent.classList.add(CLASS_NAME_COLLAPSED);
this._parent.classList.remove(CLASS_NAME_COLLAPSING);
}
}, this._config.animationSpeed);
}
(_b = this._element) === null || _b === void 0 ? void 0 : _b.dispatchEvent(event);
}
expand() {
var _a, _b;
const event = new Event(EVENT_EXPANDED);
if (this._parent) {
this._parent.classList.add(CLASS_NAME_EXPANDING);
const elm = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`${SELECTOR_CARD_BODY}, ${SELECTOR_CARD_FOOTER}`);
elm.forEach(el => {
if (el instanceof HTMLElement) {
slideDown(el, this._config.animationSpeed);
}
});
setTimeout(() => {
if (this._parent) {
this._parent.classList.remove(CLASS_NAME_COLLAPSED);
this._parent.classList.remove(CLASS_NAME_EXPANDING);
}
}, this._config.animationSpeed);
}
(_b = this._element) === null || _b === void 0 ? void 0 : _b.dispatchEvent(event);
}
remove() {
var _a;
const event = new Event(EVENT_REMOVE);
if (this._parent) {
slideUp(this._parent, this._config.animationSpeed);
}
(_a = this._element) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
}
toggle() {
var _a;
if ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.classList.contains(CLASS_NAME_COLLAPSED)) {
this.expand();
return;
}
this.collapse();
}
maximize() {
var _a;
const event = new Event(EVENT_MAXIMIZED);
if (this._parent) {
this._parent.style.height = `${this._parent.offsetHeight}px`;
this._parent.style.width = `${this._parent.offsetWidth}px`;
this._parent.style.transition = 'all .15s';
setTimeout(() => {
const htmlTag = document.querySelector('html');
if (htmlTag) {
htmlTag.classList.add(CLASS_NAME_MAXIMIZED);
}
if (this._parent) {
this._parent.classList.add(CLASS_NAME_MAXIMIZED);
if (this._parent.classList.contains(CLASS_NAME_COLLAPSED)) {
this._parent.classList.add(CLASS_NAME_WAS_COLLAPSED);
}
}
}, 150);
}
(_a = this._element) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
}
minimize() {
var _a;
const event = new Event(EVENT_MINIMIZED);
if (this._parent) {
this._parent.style.height = 'auto';
this._parent.style.width = 'auto';
this._parent.style.transition = 'all .15s';
setTimeout(() => {
var _a;
const htmlTag = document.querySelector('html');
if (htmlTag) {
htmlTag.classList.remove(CLASS_NAME_MAXIMIZED);
}
if (this._parent) {
this._parent.classList.remove(CLASS_NAME_MAXIMIZED);
if ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.classList.contains(CLASS_NAME_WAS_COLLAPSED)) {
this._parent.classList.remove(CLASS_NAME_WAS_COLLAPSED);
}
}
}, 10);
}
(_a = this._element) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
}
toggleMaximize() {
var _a;
if ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.classList.contains(CLASS_NAME_MAXIMIZED)) {
this.minimize();
return;
}
this.maximize();
}
}
/**
*
* Data Api implementation
* ====================================================
*/
onDOMContentLoaded(() => {
const collapseBtn = document.querySelectorAll(SELECTOR_DATA_COLLAPSE);
collapseBtn.forEach(btn => {
btn.addEventListener('click', event => {
event.preventDefault();
const target = event.target;
const data = new CardWidget(target, Default);
data.toggle();
});
});
const removeBtn = document.querySelectorAll(SELECTOR_DATA_REMOVE);
removeBtn.forEach(btn => {
btn.addEventListener('click', event => {
event.preventDefault();
const target = event.target;
const data = new CardWidget(target, Default);
data.remove();
});
});
const maxBtn = document.querySelectorAll(SELECTOR_DATA_MAXIMIZE);
maxBtn.forEach(btn => {
btn.addEventListener('click', event => {
event.preventDefault();
const target = event.target;
const data = new CardWidget(target, Default);
data.toggleMaximize();
});
});
});
exports.CardWidget = CardWidget;
exports.Layout = Layout;
exports.PushMenu = PushMenu;
exports.Treeview = Treeview;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,48 @@
/*
* list.js
* Copyright (c) 2022 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 <https://www.gnu.org/licenses/>.
*/
import {api} from "../../../boot/axios";
export default class Get {
/**
*
* @param identifier
* @param date
* @returns {Promise<AxiosResponse<any>>}
*/
get(identifier, date) {
let params = {date: date};
if (!date) {
return api.get('/api/v1/accounts/' + identifier);
}
return api.get('/api/v1/accounts/' + identifier, {params: params});
}
/**
*
* @param identifier
* @param page
* @returns {Promise<AxiosResponse<any>>}
*/
transactions(identifier, page) {
return api.get('/api/v1/accounts/' + identifier + '/transactions', {params: {page: page}});
}
}

View File

@ -0,0 +1,30 @@
/*
* overview.js
* Copyright (c) 2022 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 <https://www.gnu.org/licenses/>.
*/
import {api} from "../../../../boot/axios";
import {format} from "date-fns";
export default class Overview {
overview(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v1/chart/account/overview', {params: {start: startStr, end: endStr}});
}
}

View File

@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {api} from "../../boot/axios";
import {api} from "../../../boot/axios";
export default class Preferences {
getByName(name) {

View File

@ -19,7 +19,7 @@
*/
import {api} from "../../boot/axios.js";
import {api} from "../../../boot/axios.js";
export default class Summary {
get(start, end, code) {

View File

@ -0,0 +1,30 @@
/*
* overview.js
* Copyright (c) 2022 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 <https://www.gnu.org/licenses/>.
*/
import {api} from "../../../../boot/axios";
import {format} from "date-fns";
export default class Dashboard {
dashboard(start, end) {
let startStr = format(start, 'y-MM-dd');
let endStr = format(end, 'y-MM-dd');
return api.get('/api/v2/chart/account/dashboard', {params: {start: startStr, end: endStr}});
}
}

View File

@ -9,6 +9,7 @@ import axios from 'axios';
import store from "store";
import observePlugin from 'store/plugins/observe';
import Alpine from "alpinejs";
import * as bootstrap from 'bootstrap'
// add plugin to store and put in window
store.addPlugin(observePlugin);
@ -33,6 +34,9 @@ Promise.all([
store.set('end', range.end);
}
// save local in window.__ something
window.__localeId__ = values[3];
const event = new Event('firefly-iii-bootstrapped');
document.dispatchEvent(event);
window.bootstrapped = true;
@ -41,10 +45,5 @@ Promise.all([
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// include popper js
import '@popperjs/core';
// include bootstrap CSS
import * as bootstrap from 'bootstrap'
window.Alpine = Alpine

View File

@ -21,8 +21,9 @@
import './bootstrap.js';
import dates from './pages/shared/dates.js';
import boxes from './pages/dashboard/boxes.js';
import accounts from './pages/dashboard/accounts.js';
const comps = {dates, boxes};
const comps = {dates, boxes, accounts};
function loadPage(comps) {
Object.keys(comps).forEach(comp => {

View File

@ -1,80 +1 @@
import Summary from "./api/summary/index.js";
import {format} from 'date-fns'
import Alpine from "alpinejs";
//let amounts = [];
class IndexApp {
balanceBox = {foo: 'bar'};
constructor() {
console.log('IndexApp constructor');
}
init() {
console.log('IndexApp init');
this.loadBoxes();
}
loadBoxes() {
console.log('IndexApp loadBoxes');
let getter = new Summary();
let start = window.BasicStore.get('start');
let end = window.BasicStore.get('end');
// check on NULL values:
if (start !== null && end !== null) {
start = new Date(start);
end = new Date(end);
}
getter.get(format(start, 'yyyy-MM-dd'), format(end, 'yyyy-MM-dd'), null).then((response) => {
//
console.log('IndexApp done!');
console.log(response.data);
document.querySelector('#balanceAmount').innerText = 'ok dan';
//window.$refs.balanceAmount.text = 'bar!';
for (const i in response.data) {
if (response.data.hasOwnProperty(i)) {
const current = response.data[i];
if (i.startsWith('balance-in-')) {
//amounts.push(current);
console.log('Balance in: ', current);
}
}
}
});
}
}
let index = new IndexApp();
document.addEventListener("AppReady", (e) => {
index.init();
}, false,);
if (window.BasicStore.isReady()) {
index.init();
}
document.addEventListener('alpine:init', () => {
Alpine.data('balanceBox', () => ({
foo: 'barX'
}))
})
export function amounts() {
return {
amounts: ['bar', 'boo', 'baz'],
add() {
this.amounts.push('foo');
},
get() {
return this.amounts[1];
}
}
}
window.Alpine = Alpine
Alpine.start()
// NOT IN USE

View File

@ -0,0 +1,176 @@
/*
* accounts.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 <https://www.gnu.org/licenses/>.
*/
import ApexCharts from "apexcharts";
import {getVariable} from "../../store/get-variable.js";
import Dashboard from "../../api/v2/chart/account/dashboard.js";
import formatLocal from "../../util/format.js";
import {format} from "date-fns";
import formatMoney from "../../util/format-money.js";
import Get from "../../api/v1/accounts/get.js";
// this is very ugly, but I have no better ideas at the moment to save the currency info
// for each series.
window.currencies = [];
export default () => ({
loading: false,
loadingAccounts: false,
accountList: [],
chart: null,
loadChart() {
if (this.loading) {
return;
}
// load chart data
this.loading = true;
const dashboard = new Dashboard();
dashboard.dashboard(new Date(window.store.get('start')), new Date(window.store.get('end')), null).then((response) => {
// chart options (may need to be centralized later on)
window.currencies = [];
let options = {
legend: {show: false},
chart: {
height: 400,
toolbar: {tools: {zoom: false, download: false, pan: false}}, type: 'line'
}, series: [],
settings: [],
xaxis: {
categories: [], labels: {
formatter: function (value) {
if (undefined === value) {
return '';
}
const date = new Date(value);
if (date instanceof Date && !isNaN(date)) {
return formatLocal(date, 'PP');
}
console.error('Could not parse "' + value + '", return "".');
return ':(';
}
}
}, yaxis: {
labels: {
formatter: function (value, index) {
if (undefined === value) {
return value;
}
if (undefined === index) {
return value;
}
if (typeof index === 'object') {
index = index.seriesIndex;
}
//console.log(index);
let currencyCode = window.currencies[index] ?? 'EUR';
return formatMoney(value, currencyCode);
}
}
},
};
// render data:
for (let i = 0; i < response.data.length; i++) {
if (response.data.hasOwnProperty(i)) {
let current = response.data[i];
let entry = [];
window.currencies.push(current.currency_code);
for (const [ii, value] of Object.entries(current.entries)) {
entry.push({x: format(new Date(ii), 'yyyy-MM-dd'), y: parseFloat(value)});
}
options.series.push({name: current.label, data: entry});
}
}
if (null !== this.chart) {
// chart already in place, refresh:
this.chart.updateOptions(options);
}
if (null === this.chart) {
this.chart = new ApexCharts(document.querySelector("#account-chart"), options);
this.chart.render();
}
this.loading = false;
});
}, loadAccounts() {
if (this.loadingAccounts) {
return;
}
this.loadingAccounts = true;
const max = 10;
Promise.all([getVariable('frontpageAccounts'),]).then((values) => {
for (let i = 0; i < values[0].length; i++) {
if (values[0].hasOwnProperty(i)) {
let accountId = values[0][i];
// grab account info for box:
(new Get).get(accountId, new Date(window.store.get('end'))).then((response) => {
let current = response.data.data;
this.accountList[i] = {
name: current.attributes.name,
id: current.id,
balance: formatMoney(current.attributes.current_balance, current.attributes.currency_code),
groups: [],
};
// get groups for account:
(new Get).transactions(current.id, 1).then((response) => {
for (let ii = 0; ii < response.data.data.length; ii++) {
if (ii >= max) {
break;
}
let current = response.data.data[ii];
let group = {
title: null === current.attributes.group_title ? '' : current.attributes.group_title,
id: current.id,
transactions: [],
};
for (let iii = 0; iii < current.attributes.transactions.length; iii++) {
let currentTransaction = current.attributes.transactions[iii];
group.transactions.push({
description: currentTransaction.description,
id: current.id,
amount: formatMoney(currentTransaction.amount, currentTransaction.currency_code),
});
}
this.accountList[i].groups.push(group);
}
// will become false after the FIRST account is loaded.
this.loadingAccounts = false;
});
}).then(() => {
console.log(this.accountList);
});
}
}
});
},
init() {
console.log('init');
Promise.all([getVariable('viewRange'),]).then((values) => {
this.loadChart();
this.loadAccounts();
});
window.store.observe('end', () => {
this.loadChart();
this.loadAccounts();
});
},
});

View File

@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Summary from "../../api/summary/index.js";
import Summary from "../../api/v1/summary/index.js";
import {format} from "date-fns";
import {getVariable} from "../../store/get-variable.js";
@ -28,8 +28,12 @@ export default () => ({
billBox: {paid: [], unpaid: []},
leftBox: {left: [], perDay: []},
netBox: {net: []},
loading: false,
loadBoxes() {
console.log('loadboxes');
if (this.loading) {
return;
}
this.loading = true;
// get stuff
let getter = new Summary();
@ -75,22 +79,17 @@ export default () => ({
//console.log('Next up: ', current);
}
}
this.loading = false;
});
},
// Getter
init() {
console.log('Now in boxes');
Promise.all([
getVariable('viewRange'),
]).then((values) => {
Promise.all([getVariable('viewRange'),]).then((values) => {
this.loadBoxes();
});
window.store.observe('start', (newValue, oldValue) => {
// this.loadBoxes();
});
window.store.observe('end', (newValue, oldValue) => {
window.store.observe('end', () => {
this.loadBoxes();
});
},

View File

@ -24,7 +24,7 @@ export default () => ({
language: 'en-US',
init() {
console.log('Dates init');
// console.log('Dates init');
this.range = {
start: new Date(window.store.get('start')),
@ -68,7 +68,7 @@ export default () => ({
buildDateRange() {
console.log('Dates buildDateRange');
// console.log('Dates buildDateRange');
// generate ranges
let nextRange = this.getNextRange();
@ -123,7 +123,7 @@ export default () => ({
element.setAttribute('data-end', format(ytd.end, 'yyyy-MM-dd'));
// custom range.
console.log('MainApp: buildDateRange end');
// console.log('MainApp: buildDateRange end');
},
getNextRange() {
@ -161,12 +161,12 @@ export default () => ({
changeDateRange(e) {
e.preventDefault();
console.log('MainApp: changeDateRange');
// console.log('MainApp: changeDateRange');
let target = e.currentTarget;
let start = new Date(target.getAttribute('data-start'));
let end = new Date(target.getAttribute('data-end'));
console.log('MainApp: Change date range', start, end);
// console.log('MainApp: Change date range', start, end);
window.store.set('start', start);
window.store.set('end', end);

View File

@ -22,6 +22,8 @@
// Variables
//@import "variables";
$color-mode-type: media-query;
// Bootstrap
//@import "~bootstrap-sass/assets/stylesheets/bootstrap";

View File

@ -18,7 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import Get from "../api/preferences/index.js";
import Get from "../api/v1/preferences/index.js";
export function getVariable(name) {
@ -26,17 +26,17 @@ export function getVariable(name) {
// to make things available quicker than if the store has to grab it through the API.
// then again, it's not that slow.
if (window.hasOwnProperty(name)) {
console.log('Get from window');
// console.log('Get from window');
return Promise.resolve(window[name]);
}
// load from store2, if it's present.
if (window.store.get(name)) {
console.log('Get from store');
// console.log('Get from store');
return Promise.resolve(window.store.get(name));
}
let getter = (new Get);
return getter.getByName(name).then((response) => {
console.log('Get from API');
// console.log('Get from API');
return Promise.resolve(parseResponse(name, response));
});
@ -45,7 +45,7 @@ export function getVariable(name) {
function parseResponse(name, response) {
let value = response.data.data.attributes.data;
window.store.set(name, value);
console.log('Store from API');
// console.log('Store from API');
return value;
}

View File

@ -13,7 +13,7 @@ function getViewRange(viewRange, today) {
let start;
let end;
console.log('getViewRange: ' + viewRange);
// console.log('getViewRange: ' + viewRange);
switch (viewRange) {
case 'last365':
@ -100,7 +100,6 @@ function getViewRange(viewRange, today) {
end = endOfDay(end);
break;
}
console.log('MainApp: setDatesFromViewRange done!');
return {start: start, end: end};
}

View File

@ -0,0 +1,30 @@
/*
* format-money.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 <https://www.gnu.org/licenses/>.
*/
import {format} from "date-fns";
export default function (amount, currencyCode) {
let locale = window.__localeId__.replace('_', '-');
return Intl.NumberFormat(locale, {
style: 'currency',
currency: currencyCode
}).format(amount);
}

View File

@ -11,22 +11,113 @@
<!-- row with account data -->
<div class="row">
<div class="col-xl-8 col-lg-12 col-sm-12 col-xs-12">
Graph
<div class="row mb-2">
<div class="col">
<div class="card">
<div class="card-header">
<h3 class="card-title"><a href="{{ route('accounts.index',['asset']) }}"
title="{{ __('firefly.yourAccounts') }}">{{ __('firefly.yourAccounts') }}</a>
</h3>
</div>
<div class="card-body">
<div id="account-chart"></div>
</div>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col">
<div class="card">
<div class="card-header">
<h3 class="card-title"><a href="{{ route('accounts.index',['asset']) }}"
title="{{ __('firefly.yourAccounts') }}">budget</a>
</h3>
</div>
<div class="card-body">
<div id="budget-chart"></div>
</div>
</div>
</div>
</div>
<div class="row mb-2">
<div class="col">
<div class="card">
<div class="card-header">
<h3 class="card-title"><a href="{{ route('accounts.index',['asset']) }}"
title="{{ __('firefly.yourAccounts') }}">cat</a>
</h3>
</div>
<div class="card-body">
<div id="category-chart"></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4 col-lg-12 col-sm-12 col-xs-12">
<div class="row">
<div class="col-12">
Account1
</div>
<div class="col-12">
Account2
</div>
<div class="col-12">
Account3
</div>
<div class="col-12">
Account4
</div>
<div class="row" x-data="accounts">
<template x-for="account in accountList">
<div class="col-12 mb-2">
<div class="card">
<div class="card-header">
<h3 class="card-title">
<a :href="'{{ route('accounts.show','') }}/' + account.id"
x-text="account.name"></a>
<span class="small text-muted">(<span
x-text="account.balance"></span>)</span>
</h3>
</div>
<div class="card-body p-0">
<table class="table table-sm">
<tbody>
<template x-for="group in account.groups">
<tr>
<td>
<template x-if="group.title">
<span><a
:href="'{{route('transactions.show', '') }}/' + group.id"
x-text="group.title"></a><br/></span>
</template>
<template x-for="transaction in group.transactions">
<span>
<template x-if="group.title">
<span>-
<span
x-text="transaction.description"></span><br>
</span>
</template>
<template x-if="!group.title">
<span><a
:href="'{{route('transactions.show', '') }}/' + group.id"
x-text="transaction.description"></a><br>
</span>
</template>
</span>
</template>
</td>
<td style="width:30%;" class="text-end">
<template x-if="group.title">
<span><br/></span>
</template>
<template x-for="transaction in group.transactions">
<span>
<span x-text="transaction.amount"></span><br>
</span>
</template>
</td>
</tr>
</template>
</tbody>
</table>
</div>
</div>
</div>
</template>
</div>
</div>

View File

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="{{ trans('config.html_language') }}">
<!-- data-bs-theme="dark" -->
<!--begin::Head-->
@include('partials.layout.head')
<!--end::Head-->
@ -85,7 +86,7 @@
{{ __('firefly.profile') }}
</a>
<div class="dropdown-divider"></div>
<a href="#" class="dropdown-item">
<a href="{{ route('preferences.index') }}" class="dropdown-item">
<i class="fa-solid fa-user-gear me-2"></i>
{{ __('firefly.preferences') }}
</a>

View File

@ -8,6 +8,51 @@
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="color-scheme" content="light dark">
<script type="text/javascript">
/*!
* Color mode toggler for Bootstrap's docs (https://getbootstrap.com/)
* Copyright 2011-2023 The Bootstrap Authors
* Licensed under the Creative Commons Attribution 3.0 Unported License.
*/
(() => {
'use strict'
// todo store just happens to store in localStorage but if not, this would break.
const getStoredTheme = () => JSON.parse(localStorage.getItem('darkMode'))
const setStoredTheme = theme => localStorage.setItem('darkMode', theme)
const getPreferredTheme = () => {
const storedTheme = getStoredTheme()
if (storedTheme) {
return storedTheme
}
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
const setTheme = theme => {
if (theme === 'browser' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'dark')
return;
}
if (theme === 'browser' && window.matchMedia('(prefers-color-scheme: light)').matches) {
document.documentElement.setAttribute('data-bs-theme', 'light')
return;
}
document.documentElement.setAttribute('data-bs-theme', theme)
}
setTheme(getPreferredTheme())
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
const storedTheme = getStoredTheme()
if (storedTheme !== 'light' && storedTheme !== 'dark') {
setTheme(getPreferredTheme())
}
})
})()
</script>
<title>
@if($subTitle)
{{ $subTitle }} »
@ -23,7 +68,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--begin::Fonts-->
<link href="v4/css/fonts.css" rel="stylesheet">
<link href="v2/css/fonts.css" rel="stylesheet">
<!--end::Fonts-->
<!--begin::Third Party Plugin(OverlayScrollbars)-->
@ -39,7 +84,7 @@
--}}
<!--end::Third Party Plugin(Bootstrap Icons)-->
<!--begin::Required Plugin(AdminLTE)-->
<link rel="stylesheet" href="v4/css/adminlte.css">
<link rel="stylesheet" href="v2/css/adminlte.css">
<!--end::Required Plugin(AdminLTE)-->
@yield('vite')

View File

@ -12,5 +12,5 @@
crossorigin="anonymous"></script>
--}}
<!--end::Required Plugin(Bootstrap 5)--><!--begin::Required Plugin(AdminLTE)-->
<script src="v4/js/adminlte.js"></script>
<script src="v2/js/adminlte.js"></script>
<!--end::Required Plugin(AdminLTE)-->

View File

@ -4,7 +4,7 @@
<!--begin::Brand Link-->
<a href="{{route('index') }}" class="brand-link">
<!--begin::Brand Image-->
<img src="v4/i/logo.png" alt="Firefly III Logo"
<img src="v2/i/logo.png" alt="Firefly III Logo"
class="brand-image opacity-75 shadow">
<!--end::Brand Image-->
<!--begin::Brand Text-->