Basic set of stuff working!

This commit is contained in:
James Cole 2023-07-22 16:42:33 +02:00
parent 2c2dddc071
commit 190508fa54
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
17 changed files with 381 additions and 282 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@

View File

@ -0,0 +1 @@
var p=Object.defineProperty;var u=(n,t,e)=>t in n?p(n,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):n[t]=e;var l=(n,t,e)=>(u(n,typeof t!="symbol"?t+"":t,e),e);import{a as m,m as i,n as s}from"./index-bc37dda8.js";class y{get(t,e,a){return m.get("/api/v1/summary/basic",{params:{start:t,end:e,code:a}})}}class w{constructor(){l(this,"balanceBox",{foo:"bar"});console.log("IndexApp constructor")}init(){console.log("IndexApp init"),this.loadBoxes()}loadBoxes(){console.log("IndexApp loadBoxes");let t=new y,e=window.BasicStore.get("start"),a=window.BasicStore.get("end");e!==null&&a!==null&&(e=new Date(e),a=new Date(a)),t.get(s(e,"yyyy-MM-dd"),s(a,"yyyy-MM-dd"),null).then(o=>{console.log("IndexApp done!"),console.log(o.data),document.querySelector("#balanceAmount").innerText="ok dan";for(const d in o.data)if(o.data.hasOwnProperty(d)){const c=o.data[d];d.startsWith("balance-in-")&&console.log("Balance in: ",c)}})}}let r=new w;document.addEventListener("AppReady",n=>{r.init()},!1);window.BasicStore.isReady()&&r.init();document.addEventListener("alpine:init",()=>{i.data("balanceBox",()=>({foo:"barX"}))});window.Alpine=i;i.start();

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,7 @@
{ {
"_index-bc37dda8.js": {
"file": "assets/index-bc37dda8.js"
},
"node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf": { "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf": {
"file": "assets/fa-brands-400-20c4a58b.ttf", "file": "assets/fa-brands-400-20c4a58b.ttf",
"src": "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf" "src": "node_modules/@fortawesome/fontawesome-free/webfonts/fa-brands-400.ttf"
@ -24,12 +27,18 @@
"src": "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2" "src": "node_modules/@fortawesome/fontawesome-free/webfonts/fa-solid-900.woff2"
}, },
"resources/assets/v4/app.js": { "resources/assets/v4/app.js": {
"file": "assets/app-afb3782a.js", "file": "assets/app-c6b26402.js",
"imports": [
"_index-bc37dda8.js"
],
"isEntry": true, "isEntry": true,
"src": "resources/assets/v4/app.js" "src": "resources/assets/v4/app.js"
}, },
"resources/assets/v4/index.js": { "resources/assets/v4/index.js": {
"file": "assets/index-4ed993c7.js", "file": "assets/index-a835f2af.js",
"imports": [
"_index-bc37dda8.js"
],
"isEntry": true, "isEntry": true,
"src": "resources/assets/v4/index.js" "src": "resources/assets/v4/index.js"
}, },

View File

@ -25,6 +25,10 @@ export default class Preferences {
return api.get('/api/v1/preferences/' + name); return api.get('/api/v1/preferences/' + name);
} }
getByNameNow(name) {
return api.get('/api/v1/preferences/' + name);
}
postByName(name, value) { postByName(name, value) {
return api.post('/api/v1/preferences', {name: name, data: 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) // 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() { buildDateRange() {
console.log('MainApp: buildDateRange'); console.log('MainApp: buildDateRange');

View File

@ -5,8 +5,29 @@
*/ */
import axios from 'axios'; import axios from 'axios';
//import Alpine from 'alpinejs'; import store from 'store2';
import BasicStore from './store/Basic'; 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 = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 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 // include popper js
import '@popperjs/core'; import '@popperjs/core';
// include bootstrap // include bootstrap CSS
import * as bootstrap from 'bootstrap' import * as bootstrap from 'bootstrap'
/** window.Alpine = Alpine
* 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();

View File

@ -19,20 +19,23 @@
*/ */
import './bootstrap.js'; import './bootstrap.js';
import Alpine from "alpinejs"; import boxes from './pages/dashboard/boxes.js';
// move to bootstrap later on? const comps = {boxes};
window.Alpine = Alpine
import dashboard from './pages/dashboard.js'; function loadPage(comps) {
const comps = {dashboard};
//import * as comps from '/dist/demo/index.js';
Object.keys(comps).forEach(comp => { Object.keys(comps).forEach(comp => {
//let data = new comps[comp]();
console.log('Loaded component ' + comp);
let data = comps[comp](); let data = comps[comp]();
Alpine.data(comp, () => data); Alpine.data(comp, () => data);
}); });
Alpine.start(); Alpine.start();
}
// wait for load until bootstrapped event is received.
document.addEventListener('firefly-iii-bootstrapped', () => {
loadPage(comps);
});
// 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 Get from '../api/preferences/index.js';
import store from 'store2'; import store from 'store2';
/** /**
* A basic store for Firefly III persistent UI data and preferences. * A basic store for Firefly III persistent UI data and preferences.
*/ */
class Basic { const Basic = () => {
// currently availabel variables: // currently availabel variables:
viewRange = '1M'; const viewRange = '1M';
darkMode = 'browser'; const darkMode = 'browser';
language = 'en-US'; const language = 'en-US';
locale = 'en-US'; const locale = 'en-US';
// start and end are used by most pages to allow the user to browse back and forth. // start and end are used by most pages to allow the user to browse back and forth.
start = null; const start = null;
end = null; const end = null;
// others, to be used in the future. // others, to be used in the future.
listPageSize = 10; const listPageSize = 10;
currencyCode = 'AAA'; const currencyCode = 'AAA';
currencyId = '0'; const currencyId = '0';
ready = false; const ready = false;
//
// a very basic way to signal the store now contains all variables. // a very basic way to signal the store now contains all variables.
count = 0; const count = 0;
readyCount = 4; const readyCount = 4;
/** /**
* *
*/ */
constructor() { const init = () => {
console.log('Basic constructor') console.log('Basic store init')
}
/**
*
*/
init() {
console.log('Basic init')
this.loadVariable('viewRange') this.loadVariable('viewRange')
this.loadVariable('darkMode') this.loadVariable('darkMode')
this.loadVariable('language') this.loadVariable('language')
@ -49,7 +44,7 @@ class Basic {
* Load a variable, fresh or from storage. * Load a variable, fresh or from storage.
* @param name * @param name
*/ */
loadVariable(name) { const loadVariable = (name) => {
// currently unused, window.X can be used by the blade template // 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. // 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); let getter = (new Get);
getter.getByName(name).then((response) => this.parseResponse(name, response)); getter.getByName(name).then((response) => this.parseResponse(name, response));
} }
//
parseResponse(name, response) { const parseResponse = (name, response) => {
let value = response.data.data.attributes.data; let value = response.data.data.attributes.data;
this[name] = value; this[name] = value;
// TODO store.
store.set(name, value); store.set(name, value);
this.triggerReady(); this.triggerReady();
} }
//
set(name, value) { // set(name, value) {
this[name] = value; // this[name] = value;
store.set(name, value); // store.set(name, value);
} // }
//
get(name) { // get(name) {
return store.get(name, this[name]); // return store.get(name, this[name]);
} // }
//
isReady() { const isReady = () => {
return this.count === this.readyCount; return this.count === this.readyCount;
} }
triggerReady() { const triggerReady = () => {
this.count++; this.count++;
if (this.count === this.readyCount) { if (this.count === this.readyCount) {
console.log('Basic store is ready!') console.log('Basic store is ready!')
@ -99,6 +95,8 @@ class Basic {
document.dispatchEvent(event); document.dispatchEvent(event);
} }
} }
return {
init
};
} }
export const basic = Basic();
export default 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--> <!--begin::Col-->
<div class="col-lg-3 col-6"> <div class="col-lg-3 col-6">
<!--begin::Small Box Widget 1--> <!--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"> <div class="inner">
<h3 id="balanceAmount"> <h3 id="balanceAmount">
<template x-for="(amount, index) in balanceBox.amounts" :key="index"> <template x-for="(amount, index) in balanceBox.amounts" :key="index">
@ -37,7 +37,14 @@
<!--begin::Small Box Widget 2--> <!--begin::Small Box Widget 2-->
<div class="small-box text-bg-success"> <div class="small-box text-bg-success">
<div class="inner"> <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> <p><a href="{{ route('bills.index') }}">{{ __('firefly.bills_to_pay') }}</a></p>
</div> </div>
@ -45,7 +52,13 @@
<em class="fa-regular fa-calendar"></em> <em class="fa-regular fa-calendar"></em>
</span> </span>
<span class="small-box-footer"> <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> </span>
</div> </div>
<!--end::Small Box Widget 2--> <!--end::Small Box Widget 2-->
@ -55,7 +68,14 @@
<!--begin::Small Box Widget 3--> <!--begin::Small Box Widget 3-->
<div class="small-box text-bg-warning"> <div class="small-box text-bg-warning">
<div class="inner"> <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> <p><a href="{{ route('budgets.index') }}">{{ __('firefly.left_to_spend') }}</a></p>
</div> </div>
@ -73,7 +93,14 @@
<!--begin::Small Box Widget 4--> <!--begin::Small Box Widget 4-->
<div class="small-box text-bg-danger"> <div class="small-box text-bg-danger">
<div class="inner"> <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> <p>
<a href="{{ route('reports.report.default', ['allAssetAccounts','currentYearStart','currentYearEnd']) }}">{{ __('firefly.net_worth') }}</a> <a href="{{ route('reports.report.default', ['allAssetAccounts','currentYearStart','currentYearEnd']) }}">{{ __('firefly.net_worth') }}</a>