Basic set of stuff working!

This commit is contained in:
James Cole
2023-07-22 16:42:33 +02:00
parent 2c2dddc071
commit 190508fa54
17 changed files with 381 additions and 282 deletions

View File

@@ -25,6 +25,10 @@ export default class Preferences {
return api.get('/api/v1/preferences/' + name);
}
getByNameNow(name) {
return api.get('/api/v1/preferences/' + name);
}
postByName(name, value) {
return api.post('/api/v1/preferences', {name: name, data: value});
}

View File

@@ -61,102 +61,6 @@ class MainApp {
// default range is always the current period (initialized ahead)
}
setDatesFromViewRange(today) {
console.log('MainApp: setDatesFromViewRange');
let start;
let end;
let viewRange = this.viewRange;
console.log('MainApp: viewRange: ' + viewRange);
switch (viewRange) {
case 'last365':
start = startOfDay(subDays(today, 365));
end = endOfDay(today);
break;
case 'last90':
start = startOfDay(subDays(today, 90));
end = endOfDay(today);
break;
case 'last30':
start = startOfDay(subDays(today, 30));
end = endOfDay(today);
break;
case 'last7':
start = startOfDay(subDays(today, 7));
end = endOfDay(today);
break;
case 'YTD':
start = startOfYear(today);
end = endOfDay(today);
break;
case 'QTD':
start = startOfQuarter(today);
end = endOfDay(today);
break;
case 'MTD':
start = startOfMonth(today);
end = endOfDay(today);
break;
case '1D':
// today:
start = startOfDay(today);
end = endOfDay(today);
break;
case '1W':
// this week:
start = startOfDay(startOfWeek(today, {weekStartsOn: 1}));
end = endOfDay(endOfWeek(today, {weekStartsOn: 1}));
break;
case '1M':
// this month:
start = startOfDay(startOfMonth(today));
end = endOfDay(endOfMonth(today));
break;
case '3M':
// this quarter
start = startOfDay(startOfQuarter(today));
end = endOfDay(endOfQuarter(today));
break;
case '6M':
// this half-year
if (today.getMonth() <= 5) {
start = new Date(today);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(5);
end.setDate(30);
end = endOfDay(start);
}
if (today.getMonth() > 5) {
start = new Date(today);
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(11);
end.setDate(31);
end = endOfDay(start);
}
break;
case '1Y':
// this year
start = new Date(today);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
break;
}
console.log('MainApp: setDatesFromViewRange done!');
return {start: start, end: end};
}
buildDateRange() {
console.log('MainApp: buildDateRange');

View File

@@ -5,8 +5,29 @@
*/
import axios from 'axios';
//import Alpine from 'alpinejs';
import BasicStore from './store/Basic';
import store from 'store2';
import Alpine from "alpinejs";
import {getVariable} from "./store/get-variable.js";
import {getViewRange} from "./support/get-viewrange.js";
// wait for 3 promises, because we need those later on.
window.bootstrapped = false;
Promise.all([
getVariable('viewRange'),
getVariable('darkMode'),
getVariable('locale')
]).then((values) => {
if (!store.has('start') || !store.has('end')) {
// calculate new start and end, and store them.
const range = getViewRange(values[0], new Date);
store.set('start', range.start);
store.set('end', range.end);
}
const event = new Event('firefly-iii-bootstrapped');
document.dispatchEvent(event);
window.bootstrapped = true;
});
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
@@ -14,31 +35,7 @@ window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// include popper js
import '@popperjs/core';
// include bootstrap
// include bootstrap CSS
import * as bootstrap from 'bootstrap'
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo';
// import Pusher from 'pusher-js';
// window.Pusher = Pusher;
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: import.meta.env.VITE_PUSHER_APP_KEY,
// cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1',
// wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`,
// wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80,
// wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443,
// forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https',
// enabledTransports: ['ws', 'wss'],
// });
window.BasicStore = new BasicStore;
window.BasicStore.init();
window.Alpine = Alpine

View File

@@ -19,20 +19,23 @@
*/
import './bootstrap.js';
import Alpine from "alpinejs";
import boxes from './pages/dashboard/boxes.js';
// move to bootstrap later on?
window.Alpine = Alpine
const comps = {boxes};
import dashboard from './pages/dashboard.js';
function loadPage(comps) {
Object.keys(comps).forEach(comp => {
let data = comps[comp]();
Alpine.data(comp, () => data);
});
Alpine.start();
}
const comps = {dashboard};
//import * as comps from '/dist/demo/index.js';
Object.keys(comps).forEach(comp => {
//let data = new comps[comp]();
console.log('Loaded component ' + comp);
let data = comps[comp]();
Alpine.data(comp, () => data);
// wait for load until bootstrapped event is received.
document.addEventListener('firefly-iii-bootstrapped', () => {
loadPage(comps);
});
Alpine.start();
// or is bootstrapped before event is triggered.
if (window.bootstrapped) {
loadPage(comps);
}

View File

@@ -1,63 +0,0 @@
/*
* dashboard.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 Summary from "../api/summary/index.js";
import {format} from "date-fns";
export default () => ({
balanceBox: {amounts: [], subtitles: []},
constructor() {
console.log('DashboardClass constructor');
//
},
// Getter
init() {
// get boxes info.
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('DashboardClass done!');
console.log(response.data);
for (const i in response.data) {
if (response.data.hasOwnProperty(i)) {
const current = response.data[i];
if (i.startsWith('balance-in-')) {
console.log('Balance in: ', current);
this.balanceBox.amounts.push(current.value_parsed);
this.balanceBox.subtitles.push(current.sub_title);
}
}
}
});
},
});

View File

@@ -0,0 +1,82 @@
/*
* dashboard.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 Summary from "../../api/summary/index.js";
import {format} from "date-fns";
import {getVariable} from "../../store/get-variable.js";
import store from 'store2';
export default () => ({
balanceBox: {amounts: [], subtitles: []},
billBox: {paid: [], unpaid: []},
leftBox: {left: [], perDay: []},
netBox: {net: []},
constructor() {
console.log('DashboardClass constructor');
//
},
// Getter
init() {
console.log('Now in boxes');
Promise.all([
getVariable('viewRange'),
]).then((values) => {
let getter = new Summary();
let start = new Date(store.get('start'));
let end = new Date(store.get('end'));
getter.get(format(start, 'yyyy-MM-dd'), format(end, 'yyyy-MM-dd'), null).then((response) => {
for (const i in response.data) {
if (response.data.hasOwnProperty(i)) {
const current = response.data[i];
if (i.startsWith('balance-in-')) {
this.balanceBox.amounts.push(current.value_parsed);
this.balanceBox.subtitles.push(current.sub_title);
continue;
}
if (i.startsWith('bills-unpaid-in-')) {
this.billBox.unpaid.push(current.value_parsed);
continue;
}
if (i.startsWith('bills-paid-in-')) {
this.billBox.paid.push(current.value_parsed);
continue;
}
if (i.startsWith('spent-in-')) {
this.leftBox.left.push(current.value_parsed);
}
if (i.startsWith('net-worth-in-')) {
this.netBox.net.push(current.value_parsed);
}
console.log('Next up: ', current);
}
}
});
});
},
});

View File

@@ -3,42 +3,37 @@
import Get from '../api/preferences/index.js';
import store from 'store2';
/**
* A basic store for Firefly III persistent UI data and preferences.
*/
class Basic {
const Basic = () => {
// currently availabel variables:
viewRange = '1M';
darkMode = 'browser';
language = 'en-US';
locale = 'en-US';
const viewRange = '1M';
const darkMode = 'browser';
const language = 'en-US';
const locale = 'en-US';
// start and end are used by most pages to allow the user to browse back and forth.
start = null;
end = null;
const start = null;
const end = null;
// others, to be used in the future.
listPageSize = 10;
currencyCode = 'AAA';
currencyId = '0';
ready = false;
const listPageSize = 10;
const currencyCode = 'AAA';
const currencyId = '0';
const ready = false;
//
// a very basic way to signal the store now contains all variables.
count = 0;
readyCount = 4;
const count = 0;
const readyCount = 4;
/**
*
*/
constructor() {
console.log('Basic constructor')
}
/**
*
*/
init() {
console.log('Basic init')
const init = () => {
console.log('Basic store init')
this.loadVariable('viewRange')
this.loadVariable('darkMode')
this.loadVariable('language')
@@ -49,7 +44,7 @@ class Basic {
* Load a variable, fresh or from storage.
* @param name
*/
loadVariable(name) {
const loadVariable = (name) => {
// currently unused, window.X can be used by the blade template
// to make things available quicker than if the store has to grab it through the API.
@@ -69,28 +64,29 @@ class Basic {
let getter = (new Get);
getter.getByName(name).then((response) => this.parseResponse(name, response));
}
parseResponse(name, response) {
//
const parseResponse = (name, response) => {
let value = response.data.data.attributes.data;
this[name] = value;
// TODO store.
store.set(name, value);
this.triggerReady();
}
set(name, value) {
this[name] = value;
store.set(name, value);
}
get(name) {
return store.get(name, this[name]);
}
isReady() {
//
// set(name, value) {
// this[name] = value;
// store.set(name, value);
// }
//
// get(name) {
// return store.get(name, this[name]);
// }
//
const isReady = () => {
return this.count === this.readyCount;
}
triggerReady() {
const triggerReady = () => {
this.count++;
if (this.count === this.readyCount) {
console.log('Basic store is ready!')
@@ -99,6 +95,8 @@ class Basic {
document.dispatchEvent(event);
}
}
return {
init
};
}
export default Basic;
export const basic = Basic();

View File

@@ -0,0 +1,51 @@
/*
* get-variable.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 store from "store2";
import Get from "../api/preferences/index.js";
export function getVariable(name) {
// currently unused, window.X can be used by the blade template
// 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('Store from window');
return Promise.resolve(window[name]);
}
// load from store2, if it's present.
if (store.has(name)) {
console.log('Store from store2');
return Promise.resolve(store.get(name));
}
let getter = (new Get);
return getter.getByName(name).then((response) => {
return Promise.resolve(parseResponse(name, response));
});
}
function parseResponse(name, response) {
let value = response.data.data.attributes.data;
store.set(name, value);
console.log('Store from API');
return value;
}

View File

@@ -1,22 +0,0 @@
class DateRange {
start=null;
end = null;
constructor() {
this.start = null
this.end = null
}
setStart(start) {
this.start = start
}
setEnd(end) {
this.end = end
}
}
export default DateRange

View File

@@ -0,0 +1,107 @@
import {
endOfDay, endOfMonth, endOfQuarter,
endOfWeek,
startOfDay,
startOfMonth,
startOfQuarter,
startOfWeek,
startOfYear,
subDays
} from "date-fns";
function getViewRange(viewRange, today) {
let start;
let end;
console.log('getViewRange: ' + viewRange);
switch (viewRange) {
case 'last365':
start = startOfDay(subDays(today, 365));
end = endOfDay(today);
break;
case 'last90':
start = startOfDay(subDays(today, 90));
end = endOfDay(today);
break;
case 'last30':
start = startOfDay(subDays(today, 30));
end = endOfDay(today);
break;
case 'last7':
start = startOfDay(subDays(today, 7));
end = endOfDay(today);
break;
case 'YTD':
start = startOfYear(today);
end = endOfDay(today);
break;
case 'QTD':
start = startOfQuarter(today);
end = endOfDay(today);
break;
case 'MTD':
start = startOfMonth(today);
end = endOfDay(today);
break;
case '1D':
// today:
start = startOfDay(today);
end = endOfDay(today);
break;
case '1W':
// this week:
start = startOfDay(startOfWeek(today, {weekStartsOn: 1}));
end = endOfDay(endOfWeek(today, {weekStartsOn: 1}));
break;
case '1M':
// this month:
start = startOfDay(startOfMonth(today));
end = endOfDay(endOfMonth(today));
break;
case '3M':
// this quarter
start = startOfDay(startOfQuarter(today));
end = endOfDay(endOfQuarter(today));
break;
case '6M':
// this half-year
if (today.getMonth() <= 5) {
start = new Date(today);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(5);
end.setDate(30);
end = endOfDay(start);
}
if (today.getMonth() > 5) {
start = new Date(today);
start.setMonth(6);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(11);
end.setDate(31);
end = endOfDay(start);
}
break;
case '1Y':
// this year
start = new Date(today);
start.setMonth(0);
start.setDate(1);
start = startOfDay(start);
end = new Date(today);
end.setMonth(11);
end.setDate(31);
end = endOfDay(end);
break;
}
console.log('MainApp: setDatesFromViewRange done!');
return {start: start, end: end};
}
export {getViewRange};

View File

@@ -1,8 +1,8 @@
<div class="row">
<div class="row" x-data="boxes">
<!--begin::Col-->
<div class="col-lg-3 col-6">
<!--begin::Small Box Widget 1-->
<div class="small-box text-bg-primary" x-data="dashboard">
<div class="small-box text-bg-primary">
<div class="inner">
<h3 id="balanceAmount">
<template x-for="(amount, index) in balanceBox.amounts" :key="index">
@@ -37,7 +37,14 @@
<!--begin::Small Box Widget 2-->
<div class="small-box text-bg-success">
<div class="inner">
<h3>TODO amount</h3>
<h3>
<template x-for="(amount, index) in billBox.unpaid" :key="index">
<span>
<span x-text="amount"></span><span
:class="{ 'invisible': (billBox.unpaid.length == index+1) }">, </span>
</span>
</template>
</h3>
<p><a href="{{ route('bills.index') }}">{{ __('firefly.bills_to_pay') }}</a></p>
</div>
@@ -45,7 +52,13 @@
<em class="fa-regular fa-calendar"></em>
</span>
<span class="small-box-footer">
{{ __('firefly.paid') }}: TODO amount
{{ __('firefly.paid') }}:
<template x-for="(amount, index) in billBox.paid" :key="index">
<span>
<span x-text="amount"></span><span
:class="{ 'invisible': (billBox.paid.length == index+1) }">, </span>
</span>
</template>
</span>
</div>
<!--end::Small Box Widget 2-->
@@ -55,7 +68,14 @@
<!--begin::Small Box Widget 3-->
<div class="small-box text-bg-warning">
<div class="inner">
<h3>TODO amount</h3>
<h3>
<template x-for="(amount, index) in leftBox.left" :key="index">
<span>
<span x-text="amount"></span><span
:class="{ 'invisible': (leftBox.left.length == index+1) }">, </span>
</span>
</template>
</h3>
<p><a href="{{ route('budgets.index') }}">{{ __('firefly.left_to_spend') }}</a></p>
</div>
@@ -73,7 +93,14 @@
<!--begin::Small Box Widget 4-->
<div class="small-box text-bg-danger">
<div class="inner">
<h3>TODO amount</h3>
<h3>
<template x-for="(amount, index) in netBox.net" :key="index">
<span>
<span x-text="amount"></span><span
:class="{ 'invisible': (netBox.net.length == index+1) }">, </span>
</span>
</template>
</h3>
<p>
<a href="{{ route('reports.report.default', ['allAssetAccounts','currentYearStart','currentYearEnd']) }}">{{ __('firefly.net_worth') }}</a>