-
-
-
{{ buttonText }}
-
-
- Close
- Save
-
-
+
+
+ {{ buttonText }}
+
+
+
+
+ Close
+
+
+ Save
+
+
-
\ No newline at end of file
+
+
diff --git a/web/src/components/RowCard.vue b/web/src/components/RowCard.vue
new file mode 100644
index 0000000..7a2b4c6
--- /dev/null
+++ b/web/src/components/RowCard.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/web/src/components/SimpleButton.vue b/web/src/components/SimpleButton.vue
new file mode 100644
index 0000000..b18d3d9
--- /dev/null
+++ b/web/src/components/SimpleButton.vue
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/web/src/components/TransactionEditRow.vue b/web/src/components/TransactionEditRow.vue
index c7e3f4e..f864b64 100644
--- a/web/src/components/TransactionEditRow.vue
+++ b/web/src/components/TransactionEditRow.vue
@@ -5,7 +5,7 @@ import { useAccountStore } from '../stores/budget-account'
import DateInput from "./DateInput.vue";
import { useTransactionsStore } from "../stores/transactions";
import Input from "./Input.vue";
-import Button from "./Button.vue";
+import Button from "./SimpleButton.vue";
const props = defineProps<{
transactionid: string
@@ -38,29 +38,50 @@ function saveTransaction(e: MouseEvent) {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Save
-
-
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
+
+
+
+
diff --git a/web/src/components/TransactionInputRow.vue b/web/src/components/TransactionInputRow.vue
index d892eda..88ceccd 100644
--- a/web/src/components/TransactionInputRow.vue
+++ b/web/src/components/TransactionInputRow.vue
@@ -3,7 +3,7 @@ import { computed, ref } from "vue";
import Autocomplete from '../components/Autocomplete.vue'
import { Transaction, useTransactionsStore } from "../stores/transactions";
import DateInput from "./DateInput.vue";
-import Button from "./Button.vue";
+import Button from "./SimpleButton.vue";
import Input from "./Input.vue";
const props = defineProps<{
@@ -52,32 +52,53 @@ function saveTransaction(e: MouseEvent) {
-
- Date
-
-
-
- Payee
-
-
-
- Category
-
-
-
-
-
-
- Amount
-
-
-
-
- Save
-
-
-
\ No newline at end of file
+
+ Date
+
+
+
+ Payee
+
+
+
+ Category
+
+
+
+
+
+
+ Amount
+
+
+
+
+
+ Save
+
+
+
+
diff --git a/web/src/components/TransactionRow.vue b/web/src/components/TransactionRow.vue
index 0d87765..fc222ca 100644
--- a/web/src/components/TransactionRow.vue
+++ b/web/src/components/TransactionRow.vue
@@ -47,34 +47,58 @@ function getStatusSymbol() {
-
- {{ formatDate(TX.Date) }}
-
-
+
-
- {{ formatDate(TX.Date) }}
- {{ TX.TransferAccount ? "Transfer : " + TX.TransferAccount : TX.Payee }}
- {{ TX.CategoryGroup ? TX.CategoryGroup + " : " + TX.Category : "" }}
-
- {{ TX.Memo }}
-
-
-
-
-
- {{ TX.GroupID ? "☀" : "" }}
- {{ getStatusSymbol() }}
- ✎
-
-
-
-
+ {{ formatDate(TX.Date) }}
+
+
+
+
+
+ {{ formatDate(TX.Date) }}
+
+
+ {{ TX.TransferAccount ? "Transfer : " + TX.TransferAccount : TX.Payee }}
+
+
+ {{ TX.CategoryGroup ? TX.CategoryGroup + " : " + TX.Category : "" }}
+
+
+ {{ TX.Memo }}
+
+
+
+
+
+ {{ TX.GroupID ? "☀" : "" }}
+ {{ getStatusSymbol() }}
+ ✎
+
+
+
+
\ No newline at end of file
+
diff --git a/web/src/date.ts b/web/src/date.ts
index de7fccf..1382440 100644
--- a/web/src/date.ts
+++ b/web/src/date.ts
@@ -1,7 +1,8 @@
export function formatDate(date: Date): string {
- return date.toLocaleDateString(undefined, { // you can use undefined as first argument
+ return date.toLocaleDateString(undefined, {
+ // you can use undefined as first argument
year: "numeric",
month: "2-digit",
day: "2-digit",
});
-}
\ No newline at end of file
+}
diff --git a/web/src/dialogs/EditAccount.vue b/web/src/dialogs/EditAccount.vue
index d8e3515..7a2b8ca 100644
--- a/web/src/dialogs/EditAccount.vue
+++ b/web/src/dialogs/EditAccount.vue
@@ -20,7 +20,7 @@ function editAccount(e : {cancel:boolean}) : boolean {
if(CurrentAccount.value?.ClearedBalance != 0 && !accountOpen.value){
e.cancel = true;
error.value = "Cannot close account with balance";
- return false;
+ return false;
}
error.value = "";
@@ -42,35 +42,44 @@ function openEditAccount(e : any) {
-
- ✎
-
-
-
-
-
- On Budget
-
-
-
- Open
-
-
- {{ error }}
-
-
-
\ No newline at end of file
+
+
+ ✎
+
+
+
+
+
+
+ On Budget
+
+
+
+ Open
+
+
+ {{ error }}
+
+
+
diff --git a/web/src/dialogs/NewBudget.vue b/web/src/dialogs/NewBudget.vue
index 86d72d7..5e48b02 100644
--- a/web/src/dialogs/NewBudget.vue
+++ b/web/src/dialogs/NewBudget.vue
@@ -11,9 +11,18 @@ function saveBudget() {
-
+
-
+
-
\ No newline at end of file
+
diff --git a/web/src/index.css b/web/src/index.css
index 345b41c..8878239 100644
--- a/web/src/index.css
+++ b/web/src/index.css
@@ -3,15 +3,15 @@
@tailwind utilities;
h1 {
- font-size: 200%;
+ font-size: 200%;
}
a {
- text-decoration: underline;
+ text-decoration: underline;
}
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-}
\ No newline at end of file
+}
diff --git a/web/src/main.ts b/web/src/main.ts
index 7fdd123..88934e7 100644
--- a/web/src/main.ts
+++ b/web/src/main.ts
@@ -1,72 +1,79 @@
-import { createApp } from 'vue'
-import App from './App.vue'
-import './index.css'
-import router from './router'
-import { createPinia } from 'pinia'
-import { useBudgetsStore } from './stores/budget';
-import { useAccountStore } from './stores/budget-account'
-import PiniaLogger from './pinia-logger'
-import { useSessionStore } from './stores/session'
+import { createApp } from "vue";
+import App from "./App.vue";
+import "./index.css";
+import router from "./router";
+import { createPinia } from "pinia";
+import { useBudgetsStore } from "./stores/budget";
+import { useAccountStore } from "./stores/budget-account";
+import PiniaLogger from "./pinia-logger";
+import { useSessionStore } from "./stores/session";
-const app = createApp(App)
-app.use(router)
+const app = createApp(App);
+app.use(router);
-const pinia = createPinia()
-pinia.use(PiniaLogger({
- expanded: false,
- showDuration: true
-}))
-app.use(pinia)
-app.mount('#app')
+const pinia = createPinia();
+pinia.use(
+ PiniaLogger({
+ expanded: false,
+ showDuration: true,
+ })
+);
+app.use(pinia);
+app.mount("#app");
router.beforeEach(async (to, from, next) => {
- const budgetStore = useBudgetsStore();
- await budgetStore.SetCurrentBudget((
to.params.budgetid));
+ const budgetStore = useBudgetsStore();
+ await budgetStore.SetCurrentBudget(to.params.budgetid);
- const accountStore = useAccountStore();
- await accountStore.SetCurrentAccount((to.params.budgetid), (to.params.accountid));
- next();
-})
+ const accountStore = useAccountStore();
+ await accountStore.SetCurrentAccount(
+ to.params.budgetid,
+ to.params.accountid
+ );
+ next();
+});
router.beforeEach((to, from, next) => {
- const sessionStore = useSessionStore();
- const token = sessionStore.Session?.Token;
- let loggedIn = false;
+ const sessionStore = useSessionStore();
+ const token = sessionStore.Session?.Token;
+ let loggedIn = false;
- if (token != null) {
- const jwt = parseJwt(token);
- if (jwt.exp > Date.now() / 1000)
- loggedIn = true;
- }
-
- if (to.matched.some(record => record.meta.requiresAuth)) {
- if (!loggedIn) {
- next({ path: '/login' });
- } else {
- next();
+ if (token != null) {
+ const jwt = parseJwt(token);
+ if (jwt.exp > Date.now() / 1000) loggedIn = true;
}
- } else if (to.matched.some(record => record.meta.hideForAuth)) {
- if (loggedIn) {
- next({ path: '/dashboard' });
+ if (to.matched.some((record) => record.meta.requiresAuth)) {
+ if (!loggedIn) {
+ next({ path: "/login" });
+ } else {
+ next();
+ }
+ } else if (to.matched.some((record) => record.meta.hideForAuth)) {
+ if (loggedIn) {
+ next({ path: "/dashboard" });
+ } else {
+ next();
+ }
} else {
- next();
+ next();
}
- } else {
- next();
- }
});
function parseJwt(token: string) {
- var base64Url = token.split('.')[1];
- var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
- var jsonPayload = decodeURIComponent(atob(base64).split('').map(function (c) {
- return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
- }).join(''));
+ var base64Url = token.split(".")[1];
+ var base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
+ var jsonPayload = decodeURIComponent(
+ atob(base64)
+ .split("")
+ .map(function (c) {
+ return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
+ })
+ .join("")
+ );
- return JSON.parse(jsonPayload);
-};
+ return JSON.parse(jsonPayload);
+}
-
-1646426130
-1646512855755
\ No newline at end of file
+1646426130;
+1646512855755;
diff --git a/web/src/pages/Account.vue b/web/src/pages/Account.vue
index 37949bb..6dc3a85 100644
--- a/web/src/pages/Account.vue
+++ b/web/src/pages/Account.vue
@@ -5,10 +5,11 @@ import TransactionRow from "../components/TransactionRow.vue";
import TransactionInputRow from "../components/TransactionInputRow.vue";
import { useAccountStore } from "../stores/budget-account";
import EditAccount from "../dialogs/EditAccount.vue";
-import Button from "../components/Button.vue";
+import Button from "../components/SimpleButton.vue";
import { useTransactionsStore } from "../stores/transactions";
import Modal from "../components/Modal.vue";
import Input from "../components/Input.vue";
+import Checkbox from "../components/Checkbox.vue";
defineProps<{
budgetid: string
@@ -42,94 +43,124 @@ function createReconcilationTransaction() {
-
-
-
- {{ accounts.CurrentAccount?.Name }}
-
-
-
-
-
-
- Working:
-
-
-
-
- Cleared:
-
-
-
-
- Reconciled:
-
-
-
-
-
- My current balance is
-
-
-
-
- No, it's:
-
- (Difference
- )
-
- Cancel
-
-
+
+
+
+ {{ accounts.CurrentAccount?.Name }}
+
+
-
-
-
-
- +
-
-
-
+
+
+ Working:
+
+
+
+
+ Cleared:
+
+
+
+
+ Reconciled:
+
+
+
+
+
+ My current balance is
+
+
+
+
+ No, it's:
+
+ (Difference
+ )
+
+ Cancel
+
+
+
+
+
+
+ Date
+
+
+ Payee
+
+
+ Category
+
+ Memo
+
+ Amount
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
\ No newline at end of file
+
diff --git a/web/src/pages/Admin.vue b/web/src/pages/Admin.vue
index 183df55..430c5b2 100644
--- a/web/src/pages/Admin.vue
+++ b/web/src/pages/Admin.vue
@@ -8,9 +8,9 @@ onMounted(() => {
- Danger Zone
-
-
Clear database
-
This removes all data and starts from scratch. Not undoable!
-
-
\ No newline at end of file
+
Danger Zone
+
+
Clear database
+
This removes all data and starts from scratch. Not undoable!
+
+
diff --git a/web/src/pages/BudgetSidebar.vue b/web/src/pages/BudgetSidebar.vue
index 0407e76..37d85c3 100644
--- a/web/src/pages/BudgetSidebar.vue
+++ b/web/src/pages/BudgetSidebar.vue
@@ -1,9 +1,9 @@
- Budget for {{ selected.Month + 1 }}/{{ selected.Year }}
- Available balance:
-
- <<
- Current Month
- >>
-
-
-
- Leftover
- Assigned
- Activity
- Available
-
- {{ (getGroupState(group) ? "−" : "+") + " " + group.Name }}
-
-
-
-
-
- {{ category.Name }}
-
- assignedChanged(evt, category)" class="hidden sm:block mx-2 text-right" />
-
-
-
-
-
+ Budget for {{ selected.Month + 1 }}/{{ selected.Year }}
+ Available balance:
+
+
+
+ <<
+
+
+ Current Month
+
+
+ >>
+
+
+
+
+
Leftover
+
Assigned
+
Activity
+
Available
+
+ {{ (getGroupState(group) ? "−" : "+") + " " + group.Name }}
+
+
+
+
+
+
+ {{ category.Name }}
+
+ assignedChanged(evt, category)"
+ />
+
+
+
+
+
+
diff --git a/web/src/pages/Dashboard.vue b/web/src/pages/Dashboard.vue
index 1325af1..076375c 100644
--- a/web/src/pages/Dashboard.vue
+++ b/web/src/pages/Dashboard.vue
@@ -1,6 +1,6 @@
- Budgets
-
-
-
-
-
- {{budget.Name}}{{budget.ID == budgetid ? " *" : ""}}
-
-
-
-
+ Budgets
+
+
+
+
+
+ {{ budget.Name
+ }}{{ budget.ID == budgetid ? " *" : "" }}
+
+
+
+
diff --git a/web/src/pages/Index.vue b/web/src/pages/Index.vue
deleted file mode 100644
index d16481a..0000000
--- a/web/src/pages/Index.vue
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
- Willkommen bei Budgeteer, der neuen App für's Budget!
-
-
- Login or register
-
-
-
\ No newline at end of file
diff --git a/web/src/pages/Login.vue b/web/src/pages/Login.vue
index 0914df1..91f1423 100644
--- a/web/src/pages/Login.vue
+++ b/web/src/pages/Login.vue
@@ -28,18 +28,32 @@ function formSubmit(e: MouseEvent) {
-
-
-
-
- {{ error }}
- Login
-
- New user?
- Register instead!
-
-
\ No newline at end of file
+
+
+
+
+
{{ error }}
+
+ Login
+
+
+ New user?
+
+ Register
+ instead!
+
+
diff --git a/web/src/pages/Register.vue b/web/src/pages/Register.vue
index cc4a1ec..a58ef3d 100644
--- a/web/src/pages/Register.vue
+++ b/web/src/pages/Register.vue
@@ -1,48 +1,65 @@
-
-
-
-
-
-
-
-
- {{ error }}
- Register
-
- Existing user?
- Login instead!
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+ {{ error }}
+
+ Register
+
+
+ Existing user?
+
+ Login
+ instead!
+
+
diff --git a/web/src/pages/Settings.vue b/web/src/pages/Settings.vue
index 21dab9a..dd2b55d 100644
--- a/web/src/pages/Settings.vue
+++ b/web/src/pages/Settings.vue
@@ -4,8 +4,8 @@ import { useRouter } from "vue-router";
import { DELETE, POST } from "../api";
import { useBudgetsStore } from "../stores/budget";
import { useSessionStore } from "../stores/session";
-import Card from "../components/Card.vue";
-import Button from "../components/Button.vue";
+import RowCard from "../components/RowCard.vue";
+import Button from "../components/SimpleButton.vue";
import { saveAs } from 'file-saver';
import Input from "../components/Input.vue";
@@ -72,53 +72,105 @@ function ynabExport() {
saveAs(blob, timeStamp + " " + CurrentBudgetName.value + " - Transactions.tsv");
})
}
-
-
-
Danger Zone
-
-
- Clear Budget
- This removes transactions and assignments to start from scratch. Accounts and categories are kept. Not undoable!
+
+
Danger Zone
+
+
+
+ Clear Budget
+
+
+ This removes transactions and assignments to start from
+ scratch. Accounts and categories are kept. Not undoable!
+
- Clear budget
-
-
- Delete Budget
- This deletes the whole bugdet including all transactions, assignments, accounts and categories. Not undoable!
- Delete budget
-
-
- Fix all historic negative category-balances
- This restores YNABs functionality, that would substract any overspent categories' balances from next months inflows.
- Fix negative
-
-
- Import YNAB Budget
-
-
-
- Transaktionen:
-
-
-
-
- Budget:
-
-
-
+
+ Clear budget
+
+
+
+
+ Delete Budget
+
+
+ This deletes the whole bugdet including all transactions,
+ assignments, accounts and categories. Not undoable!
+
+
+ Delete budget
+
+
+
+
+ Fix all historic negative category-balances
+
+
+ This restores YNABs functionality, that would substract any
+ overspent categories' balances from next months inflows.
+
+
+ Fix negative
+
+
+
+
+ Import YNAB Budget
+
- Importieren
-
-
- Export as YNAB TSV
-
-
- Export
-
-
+
+
+ Transaktionen:
+
+
+
+
+ Budget:
+
+
+
+
+ Importieren
+
+
+
+
+ Export as YNAB TSV
+
+
+
+
+ Export
+
+
+
-
\ No newline at end of file
+
+
diff --git a/web/src/pages/WelcomePage.vue b/web/src/pages/WelcomePage.vue
new file mode 100644
index 0000000..5dbd548
--- /dev/null
+++ b/web/src/pages/WelcomePage.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+ Willkommen bei Budgeteer, der neuen App für's Budget!
+
+
+
+ Login
+ or
+
+ register
+
+
+
+
diff --git a/web/src/pinia-logger.ts b/web/src/pinia-logger.ts
index ff30b98..50d7974 100644
--- a/web/src/pinia-logger.ts
+++ b/web/src/pinia-logger.ts
@@ -1,81 +1,107 @@
-import { PiniaPluginContext, StoreGeneric, _ActionsTree, _StoreOnActionListenerContext } from 'pinia';
+import {
+ PiniaPluginContext,
+ StoreGeneric,
+ _ActionsTree,
+ _StoreOnActionListenerContext,
+} from "pinia";
const cloneDeep = (obj: T): T => {
- try {
- return JSON.parse(JSON.stringify(obj));
- } catch {
- return { ...obj };
- }
+ try {
+ return JSON.parse(JSON.stringify(obj));
+ } catch {
+ return { ...obj };
+ }
};
const formatTime = (date = new Date()) => {
- const hours = date.getHours().toString().padStart(2, '0');
- const minutes = date.getMinutes().toString().padStart(2, '0');
- const seconds = date.getSeconds().toString().padStart(2, '0');
+ const hours = date.getHours().toString().padStart(2, "0");
+ const minutes = date.getMinutes().toString().padStart(2, "0");
+ const seconds = date.getSeconds().toString().padStart(2, "0");
- return `${hours}:${minutes}:${seconds}`;
+ return `${hours}:${minutes}:${seconds}`;
};
export interface PiniaLoggerOptions {
- disabled?: boolean;
- expanded?: boolean;
- showDuration?: boolean
- showStoreName?: boolean;
- logErrors?: boolean;
+ disabled?: boolean;
+ expanded?: boolean;
+ showDuration?: boolean;
+ showStoreName?: boolean;
+ logErrors?: boolean;
}
-export type PiniaActionListenerContext = _StoreOnActionListenerContext;
+export type PiniaActionListenerContext = _StoreOnActionListenerContext<
+ StoreGeneric,
+ string,
+ _ActionsTree
+>;
const defaultOptions: PiniaLoggerOptions = {
- logErrors: true,
- disabled: false,
- expanded: true,
- showStoreName: true,
- showDuration: false,
+ logErrors: true,
+ disabled: false,
+ expanded: true,
+ showStoreName: true,
+ showDuration: false,
};
-export const PiniaLogger = (config = defaultOptions) => (ctx: PiniaPluginContext) => {
- const options = {
- ...defaultOptions,
- ...config,
- };
+export const PiniaLogger =
+ (config = defaultOptions) =>
+ (ctx: PiniaPluginContext) => {
+ const options = {
+ ...defaultOptions,
+ ...config,
+ };
- if (options.disabled) return;
+ if (options.disabled) return;
+ ctx.store.$onAction((action: PiniaActionListenerContext) => {
+ const startTime = Date.now();
+ const prevState = cloneDeep(ctx.store.$state);
- ctx.store.$onAction((action: PiniaActionListenerContext) => {
- const startTime = Date.now();
- const prevState = cloneDeep(ctx.store.$state);
+ const log = (isError?: boolean, error?: any) => {
+ const endTime = Date.now();
+ const duration = endTime - startTime + "ms";
+ const nextState = cloneDeep(ctx.store.$state);
+ const storeName = action.store.$id;
+ const title = `${formatTime()} action 🍍 ${
+ options.showStoreName ? `[${storeName}] ` : ""
+ }${action.name} ${
+ isError ? `failed after ` : ""
+ }in ${duration}`;
- const log = (isError?: boolean, error?: any) => {
- const endTime = Date.now();
- const duration = endTime - startTime + 'ms';
- const nextState = cloneDeep(ctx.store.$state);
- const storeName = action.store.$id;
- const title = `${formatTime()} action 🍍 ${options.showStoreName ? `[${storeName}] ` : ''}${action.name} ${isError ? `failed after ` : ''}in ${duration}`;
+ console[options.expanded ? "group" : "groupCollapsed"](
+ `%c${title}`,
+ `font-weight: bold; ${isError ? "color: #ed4981;" : ""}`
+ );
+ console.log(
+ "%cprev state",
+ "font-weight: bold; color: grey;",
+ prevState
+ );
+ console.log("%caction", "font-weight: bold; color: #69B7FF;", {
+ type: action.name,
+ args:
+ action.args.length > 0 ? { ...action.args } : undefined,
+ ...(options.showStoreName && { store: action.store.$id }),
+ ...(options.showDuration && { duration }),
+ ...(isError && { error }),
+ });
+ console.log(
+ "%cnext state",
+ "font-weight: bold; color: #4caf50;",
+ nextState
+ );
+ console.groupEnd();
+ };
- console[options.expanded ? 'group' : 'groupCollapsed'](`%c${title}`, `font-weight: bold; ${isError ? 'color: #ed4981;' : ''}`);
- console.log('%cprev state', 'font-weight: bold; color: grey;', prevState);
- console.log('%caction', 'font-weight: bold; color: #69B7FF;', {
- type: action.name,
- args: action.args.length > 0 ? { ...action.args } : undefined,
- ...(options.showStoreName && { store: action.store.$id }),
- ...(options.showDuration && { duration }),
- ...(isError && { error }),
- });
- console.log('%cnext state', 'font-weight: bold; color: #4caf50;', nextState);
- console.groupEnd();
+ action.after(() => {
+ log();
+ });
+
+ if (options.logErrors) {
+ action.onError((error) => {
+ log(true, error);
+ });
+ }
+ });
};
- action.after(() => {
- log();
- });
-
- if (options.logErrors) {
- action.onError((error) => {
- log(true, error);
- });
- }
- });
-};
-
-export default PiniaLogger;
\ No newline at end of file
+export default PiniaLogger;
diff --git a/web/src/router/index.ts b/web/src/router/index.ts
index 03b9870..773dbda 100644
--- a/web/src/router/index.ts
+++ b/web/src/router/index.ts
@@ -1,30 +1,75 @@
-import { createRouter, createWebHistory, RouteLocationNormalized } from 'vue-router'
-import Dashboard from '../pages/Dashboard.vue';
-import Login from '../pages/Login.vue';
-import Index from '../pages/Index.vue';
-import Register from '../pages/Register.vue';
-import Account from '@/pages/Account.vue';
-import Settings from '../pages/Settings.vue';
-import Budgeting from '../pages/Budgeting.vue';
-import BudgetSidebar from '../pages/BudgetSidebar.vue';
+import {
+ createRouter,
+ createWebHistory,
+ RouteLocationNormalized,
+} from "vue-router";
+import Account from "@/pages/Account.vue";
+import Budgeting from "../pages/Budgeting.vue";
+import BudgetSidebar from "../pages/BudgetSidebar.vue";
+import Dashboard from "../pages/Dashboard.vue";
+import Login from "../pages/Login.vue";
+import Register from "../pages/Register.vue";
+import Settings from "../pages/Settings.vue";
+import WelcomePage from "../pages/WelcomePage.vue";
const routes = [
- { path: "/", name: "Index", component: Index },
- { path: "/dashboard", name: "Dashboard", component: Dashboard, meta: { requiresAuth: true } },
- { path: "/login", name: "Login", component: Login, meta: { hideForAuth: true } },
- { path: "/register", name: "Register", component: Register, meta: { hideForAuth: true } },
- { path: "/budget/:budgetid/budgeting", name: "Budget", redirect: (to : RouteLocationNormalized) =>
- '/budget/' + to.params.budgetid + '/budgeting/' + new Date().getFullYear() + '/' + new Date().getMonth(),
- meta: { requiresAuth: true }
- },
- { path: "/budget/:budgetid/budgeting/:year/:month", name: "Budget with date", components: { default: Budgeting, sidebar: BudgetSidebar }, props: true, meta: { requiresAuth: true } },
- { path: "/budget/:budgetid/Settings", name: "Budget Settings", components: { default: Settings, sidebar: BudgetSidebar }, props: true, meta: { requiresAuth: true } },
- { path: "/budget/:budgetid/account/:accountid", name: "Account", components: { default: Account, sidebar: BudgetSidebar }, props: true, meta: { requiresAuth: true } },
-]
+ { path: "/", name: "Index", component: WelcomePage },
+ {
+ path: "/dashboard",
+ name: "Dashboard",
+ component: Dashboard,
+ meta: { requiresAuth: true },
+ },
+ {
+ path: "/login",
+ name: "Login",
+ component: Login,
+ meta: { hideForAuth: true },
+ },
+ {
+ path: "/register",
+ name: "Register",
+ component: Register,
+ meta: { hideForAuth: true },
+ },
+ {
+ path: "/budget/:budgetid/budgeting",
+ name: "Budget",
+ redirect: (to: RouteLocationNormalized) =>
+ "/budget/" +
+ to.params.budgetid +
+ "/budgeting/" +
+ new Date().getFullYear() +
+ "/" +
+ new Date().getMonth(),
+ meta: { requiresAuth: true },
+ },
+ {
+ path: "/budget/:budgetid/budgeting/:year/:month",
+ name: "Budget with date",
+ components: { default: Budgeting, sidebar: BudgetSidebar },
+ props: true,
+ meta: { requiresAuth: true },
+ },
+ {
+ path: "/budget/:budgetid/Settings",
+ name: "Budget Settings",
+ components: { default: Settings, sidebar: BudgetSidebar },
+ props: true,
+ meta: { requiresAuth: true },
+ },
+ {
+ path: "/budget/:budgetid/account/:accountid",
+ name: "Account",
+ components: { default: Account, sidebar: BudgetSidebar },
+ props: true,
+ meta: { requiresAuth: true },
+ },
+];
const router = createRouter({
- history: createWebHistory(),
- routes,
-})
+ history: createWebHistory(),
+ routes,
+});
-export default router
\ No newline at end of file
+export default router;
diff --git a/web/src/stores/budget-account.ts b/web/src/stores/budget-account.ts
index e582248..d5b2c30 100644
--- a/web/src/stores/budget-account.ts
+++ b/web/src/stores/budget-account.ts
@@ -1,41 +1,41 @@
-import { defineStore } from "pinia"
+import { defineStore } from "pinia";
import { GET, POST } from "../api";
import { useBudgetsStore } from "./budget";
import { useSessionStore } from "./session";
import { useTransactionsStore } from "./transactions";
interface State {
- Accounts: Map
- CurrentAccountID: string | null
- Categories: Map
- Months: Map>>
- Assignments: []
+ Accounts: Map;
+ CurrentAccountID: string | null;
+ Categories: Map;
+ Months: Map>>;
+ Assignments: [];
}
export interface Account {
- ID: string
- Name: string
- OnBudget: boolean
- IsOpen: boolean
- ClearedBalance: number
- WorkingBalance: number
- ReconciledBalance: number
- Transactions: string[]
- LastReconciled: NullDate
+ ID: string;
+ Name: string;
+ OnBudget: boolean;
+ IsOpen: boolean;
+ ClearedBalance: number;
+ WorkingBalance: number;
+ ReconciledBalance: number;
+ Transactions: string[];
+ LastReconciled: NullDate;
}
interface NullDate {
- Valid: boolean
- Time: Date
+ Valid: boolean;
+ Time: Date;
}
export interface Category {
- ID: string
- Group: string
- Name: string
- AvailableLastMonth: number
- Assigned: number
- Activity: number
+ ID: string;
+ Group: string;
+ Name: string;
+ AvailableLastMonth: number;
+ Assigned: number;
+ Activity: number;
}
export const useAccountStore = defineStore("budget/account", {
@@ -53,12 +53,16 @@ export const useAccountStore = defineStore("budget/account", {
AllCategoriesForMonth: (state) => (year: number, month: number) => {
const yearMap = state.Months.get(year);
const monthMap = yearMap?.get(month);
- return [...monthMap?.values() || []];
+ return [...(monthMap?.values() || [])];
},
GetCategoryAvailable(state) {
return (category: Category): number => {
- return category.AvailableLastMonth + Number(category.Assigned) + category.Activity;
- }
+ return (
+ category.AvailableLastMonth +
+ Number(category.Assigned) +
+ category.Activity
+ );
+ };
},
GetIncomeCategoryID(state) {
const budget = useBudgetsStore();
@@ -67,15 +71,15 @@ export const useAccountStore = defineStore("budget/account", {
GetIncomeAvailable(state) {
return (year: number, month: number) => {
const IncomeCategoryID = this.GetIncomeCategoryID;
- if (IncomeCategoryID == null)
- return 0;
+ if (IncomeCategoryID == null) return 0;
const categories = this.AllCategoriesForMonth(year, month);
- const category = categories.filter(x => x.ID == IncomeCategoryID)[0];
- if (category == null)
- return 0;
+ const category = categories.filter(
+ (x) => x.ID == IncomeCategoryID
+ )[0];
+ if (category == null) return 0;
return category.AvailableLastMonth;
- }
+ };
},
CategoryGroupsForMonth(state) {
return (year: number, month: number) => {
@@ -83,8 +87,7 @@ export const useAccountStore = defineStore("budget/account", {
const categoryGroups = [];
let prev = undefined;
for (const category of categories) {
- if (category.ID == this.GetIncomeCategoryID)
- continue;
+ if (category.ID == this.GetIncomeCategoryID) continue;
if (prev == undefined || category.Group != prev.Name) {
prev = {
@@ -93,81 +96,110 @@ export const useAccountStore = defineStore("budget/account", {
AvailableLastMonth: category.AvailableLastMonth,
Activity: category.Activity,
Assigned: category.Assigned,
- }
+ };
categoryGroups.push({
...prev,
Expand: prev.Name != "Hidden Categories",
});
} else {
- categoryGroups[categoryGroups.length-1].Available += this.GetCategoryAvailable(category);
- categoryGroups[categoryGroups.length-1].AvailableLastMonth += category.AvailableLastMonth;
- categoryGroups[categoryGroups.length-1].Activity += category.Activity;
- categoryGroups[categoryGroups.length-1].Assigned += category.Assigned;
+ categoryGroups[categoryGroups.length - 1].Available +=
+ this.GetCategoryAvailable(category);
+ categoryGroups[
+ categoryGroups.length - 1
+ ].AvailableLastMonth += category.AvailableLastMonth;
+ categoryGroups[categoryGroups.length - 1].Activity +=
+ category.Activity;
+ categoryGroups[categoryGroups.length - 1].Assigned +=
+ category.Assigned;
continue;
}
}
return categoryGroups;
- }
+ };
},
CategoriesForMonthAndGroup(state) {
return (year: number, month: number, group: string) => {
const categories = this.AllCategoriesForMonth(year, month);
- return categories.filter(x => x.Group == group);
- }
+ return categories.filter((x) => x.Group == group);
+ };
},
GetAccount(state) {
return (accountid: string) => {
return this.Accounts.get(accountid);
- }
+ };
},
CurrentAccount(state): Account | undefined {
- if (state.CurrentAccountID == null)
- return undefined;
+ if (state.CurrentAccountID == null) return undefined;
return this.GetAccount(state.CurrentAccountID);
},
OnBudgetAccounts(state) {
- return [...state.Accounts.values()].filter(x => x.OnBudget);
+ return [...state.Accounts.values()].filter((x) => x.OnBudget);
},
OnBudgetAccountsBalance(state): number {
- return this.OnBudgetAccounts.reduce((prev, curr) => prev + Number(curr.ClearedBalance), 0);
+ return this.OnBudgetAccounts.reduce(
+ (prev, curr) => prev + Number(curr.ClearedBalance),
+ 0
+ );
},
OffBudgetAccounts(state) {
- return [...state.Accounts.values()].filter(x => !x.OnBudget);
+ return [...state.Accounts.values()].filter((x) => !x.OnBudget);
},
OffBudgetAccountsBalance(state): number {
- return this.OffBudgetAccounts.reduce((prev, curr) => prev + Number(curr.ClearedBalance), 0);
+ return this.OffBudgetAccounts.reduce(
+ (prev, curr) => prev + Number(curr.ClearedBalance),
+ 0
+ );
},
},
actions: {
async SetCurrentAccount(budgetid: string, accountid: string) {
- if (budgetid == null)
- return;
+ if (budgetid == null) return;
this.CurrentAccountID = accountid;
- if (accountid == null)
- return;
+ if (accountid == null) return;
const account = this.GetAccount(accountid)!;
useSessionStore().setTitle(account.Name);
await this.FetchAccount(account);
},
async FetchAccount(account: Account) {
- const result = await GET("/account/" + account.ID + "/transactions");
+ const result = await GET(
+ "/account/" + account.ID + "/transactions"
+ );
const response = await result.json();
- const transactionsStore = useTransactionsStore()
- const transactions = transactionsStore.AddTransactions(response.Transactions);
+ const transactionsStore = useTransactionsStore();
+ const transactions = transactionsStore.AddTransactions(
+ response.Transactions
+ );
account.Transactions = transactions;
},
async FetchMonthBudget(budgetid: string, year: number, month: number) {
- const result = await GET("/budget/" + budgetid + "/" + year + "/" + (month + 1));
+ const result = await GET(
+ "/budget/" + budgetid + "/" + year + "/" + (month + 1)
+ );
const response = await result.json();
- if (response.Categories == undefined || response.Categories.length <= 0)
+ if (
+ response.Categories == undefined ||
+ response.Categories.length <= 0
+ )
return;
this.addCategoriesForMonth(year, month, response.Categories);
},
- async EditAccount(accountid: string, name: string, onBudget: boolean, isOpen: boolean) {
- const result = await POST("/account/" + accountid, JSON.stringify({ name: name, onBudget: onBudget, isOpen: isOpen }));
+ async EditAccount(
+ accountid: string,
+ name: string,
+ onBudget: boolean,
+ isOpen: boolean
+ ) {
+ const result = await POST(
+ "/account/" + accountid,
+ JSON.stringify({
+ name: name,
+ onBudget: onBudget,
+ isOpen: isOpen,
+ })
+ );
const response = await result.json();
useBudgetsStore().MergeBudgetingData(response);
@@ -175,10 +207,17 @@ export const useAccountStore = defineStore("budget/account", {
this.Accounts.delete(accountid);
}
},
- addCategoriesForMonth(year: number, month: number, categories: Category[]): void {
+ addCategoriesForMonth(
+ year: number,
+ month: number,
+ categories: Category[]
+ ): void {
this.$patch((state) => {
- const yearMap = state.Months.get(year) || new Map>();
- const monthMap = yearMap.get(month) || new Map();
+ const yearMap =
+ state.Months.get(year) ||
+ new Map>();
+ const monthMap =
+ yearMap.get(month) || new Map();
for (const category of categories) {
monthMap.set(category.ID, category);
}
@@ -188,8 +227,7 @@ export const useAccountStore = defineStore("budget/account", {
});
},
logout() {
- this.$reset()
+ this.$reset();
},
- }
-
-})
+ },
+});
diff --git a/web/src/stores/budget.ts b/web/src/stores/budget.ts
index 49b8310..fe94d29 100644
--- a/web/src/stores/budget.ts
+++ b/web/src/stores/budget.ts
@@ -4,17 +4,16 @@ import { useAccountStore } from "./budget-account";
import { Budget, useSessionStore } from "./session";
interface State {
- CurrentBudgetID: string | null,
+ CurrentBudgetID: string | null;
}
-export const useBudgetsStore = defineStore('budget', {
+export const useBudgetsStore = defineStore("budget", {
state: (): State => ({
CurrentBudgetID: null,
}),
getters: {
CurrentBudget(): Budget | undefined {
- if (this.CurrentBudgetID == null)
- return undefined;
+ if (this.CurrentBudgetID == null) return undefined;
const sessionStore = useSessionStore();
return sessionStore.Budgets.get(this.CurrentBudgetID);
@@ -27,7 +26,7 @@ export const useBudgetsStore = defineStore('budget', {
ImportYNAB(formData: FormData) {
return POST(
"/budget/" + this.CurrentBudgetID + "/import/ynab",
- formData,
+ formData
);
},
async NewBudget(budgetName: string): Promise {
@@ -43,8 +42,7 @@ export const useBudgetsStore = defineStore('budget', {
async SetCurrentBudget(budgetid: string): Promise {
this.CurrentBudgetID = budgetid;
- if (budgetid == null)
- return
+ if (budgetid == null) return;
await this.FetchBudget(budgetid);
},
@@ -58,13 +56,15 @@ export const useBudgetsStore = defineStore('budget', {
for (const account of response.Accounts || []) {
const existingAccount = accounts.Accounts.get(account.ID);
account.Transactions = existingAccount?.Transactions ?? [];
- if(account.LastReconciled.Valid)
- account.LastReconciled.Time = new Date(account.LastReconciled.Time);
+ if (account.LastReconciled.Valid)
+ account.LastReconciled.Time = new Date(
+ account.LastReconciled.Time
+ );
accounts.Accounts.set(account.ID, account);
}
for (const category of response.Categories || []) {
accounts.Categories.set(category.ID, category);
}
},
- }
-})
\ No newline at end of file
+ },
+});
diff --git a/web/src/stores/session.ts b/web/src/stores/session.ts
index 5b8d690..852e6c3 100644
--- a/web/src/stores/session.ts
+++ b/web/src/stores/session.ts
@@ -1,43 +1,52 @@
-import { StorageSerializers, useStorage } from '@vueuse/core';
-import { defineStore } from 'pinia'
-import { POST } from '../api';
+import { StorageSerializers, useStorage } from "@vueuse/core";
+import { defineStore } from "pinia";
+import { POST } from "../api";
interface State {
- Session: Session | null
- Budgets: Map,
+ Session: Session | null;
+ Budgets: Map;
}
interface Session {
- Token: string
- User: string
+ Token: string;
+ User: string;
}
export interface Budget {
- ID: string
- Name: string
- AvailableBalance: number
- IncomeCategoryID: string
+ ID: string;
+ Name: string;
+ AvailableBalance: number;
+ IncomeCategoryID: string;
}
-export const useSessionStore = defineStore('session', {
+export const useSessionStore = defineStore("session", {
state: () => ({
- Session: useStorage('session', null, undefined, { serializer: StorageSerializers.object }),
- Budgets: useStorage>('budgets', new Map(), undefined, { serializer: StorageSerializers.map }),
+ Session: useStorage("session", null, undefined, {
+ serializer: StorageSerializers.object,
+ }),
+ Budgets: useStorage>(
+ "budgets",
+ new Map(),
+ undefined,
+ { serializer: StorageSerializers.map }
+ ),
}),
getters: {
- BudgetsList: (state) => [ ...state.Budgets.values() ],
- AuthHeaders: (state) => ({'Authorization': 'Bearer ' + state.Session?.Token}),
+ BudgetsList: (state) => [...state.Budgets.values()],
+ AuthHeaders: (state) => ({
+ Authorization: "Bearer " + state.Session?.Token,
+ }),
LoggedIn: (state) => state.Session != null,
},
actions: {
- setTitle(title : string) {
+ setTitle(title: string) {
document.title = "Budgeteer - " + title;
},
- loginSuccess(x : any) {
+ loginSuccess(x: any) {
this.Session = {
User: x.User,
Token: x.Token,
- }
+ };
for (const budget of x.Budgets) {
this.Budgets.set(budget.ID, budget);
}
@@ -48,8 +57,11 @@ export const useSessionStore = defineStore('session', {
this.loginSuccess(result);
return result;
},
- async register(login : any) {
- const response = await POST("/user/register", JSON.stringify(login));
+ async register(login: any) {
+ const response = await POST(
+ "/user/register",
+ JSON.stringify(login)
+ );
const result = await response.json();
this.loginSuccess(result);
return result;
@@ -58,5 +70,5 @@ export const useSessionStore = defineStore('session', {
this.Session = null;
this.Budgets.clear();
},
- }
-})
\ No newline at end of file
+ },
+});
diff --git a/web/src/stores/settings.ts b/web/src/stores/settings.ts
index c63e70a..783c936 100644
--- a/web/src/stores/settings.ts
+++ b/web/src/stores/settings.ts
@@ -2,17 +2,17 @@ import { useStorage } from "@vueuse/core";
import { defineStore } from "pinia";
interface State {
- Menu: MenuSettings
+ Menu: MenuSettings;
}
interface MenuSettings {
- Show: boolean | null,
- Expand: boolean | null,
+ Show: boolean | null;
+ Expand: boolean | null;
}
-export const useSettingsStore = defineStore('settings', {
+export const useSettingsStore = defineStore("settings", {
state: () => ({
- Menu: useStorage('settings', {
+ Menu: useStorage("settings", {
Show: null,
Expand: false,
}),
@@ -24,5 +24,5 @@ export const useSettingsStore = defineStore('settings', {
toggleMenuSize() {
this.Menu.Expand = !this.Menu.Expand;
},
- }
-});
\ No newline at end of file
+ },
+});
diff --git a/web/src/stores/transactions.ts b/web/src/stores/transactions.ts
index e8c9825..c9c8d58 100644
--- a/web/src/stores/transactions.ts
+++ b/web/src/stores/transactions.ts
@@ -1,26 +1,26 @@
-import { defineStore } from "pinia"
+import { defineStore } from "pinia";
import { POST } from "../api";
import { useAccountStore } from "./budget-account";
interface State {
- Transactions: Map
- Reconciling: boolean
+ Transactions: Map;
+ Reconciling: boolean;
}
export interface Transaction {
- ID: string
- Date: Date
- TransferAccount: string
- CategoryGroup: string
- Category: string
- CategoryID: string | undefined
- Memo: string
- Status: string
- GroupID: string
- Payee: string
- PayeeID: string | undefined
- Amount: number
- Reconciled: boolean
+ ID: string;
+ Date: Date;
+ TransferAccount: string;
+ CategoryGroup: string;
+ Category: string;
+ CategoryID: string | undefined;
+ Memo: string;
+ Status: string;
+ GroupID: string;
+ Payee: string;
+ PayeeID: string | undefined;
+ Amount: number;
+ Reconciled: boolean;
}
export const useTransactionsStore = defineStore("budget/transactions", {
@@ -30,8 +30,9 @@ export const useTransactionsStore = defineStore("budget/transactions", {
}),
getters: {
ReconcilingBalance(state): number {
- const accountsStore = useAccountStore()
- let reconciledBalance = accountsStore.CurrentAccount!.ReconciledBalance;
+ const accountsStore = useAccountStore();
+ let reconciledBalance =
+ accountsStore.CurrentAccount!.ReconciledBalance;
for (const transaction of this.TransactionsList) {
if (transaction.Reconciled)
reconciledBalance += transaction.Amount;
@@ -39,9 +40,9 @@ export const useTransactionsStore = defineStore("budget/transactions", {
return reconciledBalance;
},
TransactionsList(state): Transaction[] {
- const accountsStore = useAccountStore()
- return accountsStore.CurrentAccount!.Transactions.map(x => {
- return this.Transactions.get(x)!
+ const accountsStore = useAccountStore();
+ return accountsStore.CurrentAccount!.Transactions.map((x) => {
+ return this.Transactions.get(x)!;
});
},
},
@@ -59,25 +60,30 @@ export const useTransactionsStore = defineStore("budget/transactions", {
},
SetReconciledForAllTransactions(value: boolean) {
for (const transaction of this.TransactionsList) {
- if (transaction.Status == "Reconciled")
- continue;
+ if (transaction.Status == "Reconciled") continue;
transaction.Reconciled = value;
}
},
async SubmitReconcilation(reconciliationTransactionAmount: number) {
- const accountsStore = useAccountStore()
+ const accountsStore = useAccountStore();
const account = accountsStore.CurrentAccount!;
- const reconciledTransactions = this.TransactionsList.filter(x => x.Reconciled);
+ const reconciledTransactions = this.TransactionsList.filter(
+ (x) => x.Reconciled
+ );
for (const transaction of reconciledTransactions) {
account.ReconciledBalance += transaction.Amount;
transaction.Status = "Reconciled";
transaction.Reconciled = false;
}
- const result = await POST("/account/" + accountsStore.CurrentAccountID + "/reconcile", JSON.stringify({
- transactionIDs: reconciledTransactions.map(x => x.ID),
- reconciliationTransactionAmount: reconciliationTransactionAmount.toString(),
- }));
+ const result = await POST(
+ "/account/" + accountsStore.CurrentAccountID + "/reconcile",
+ JSON.stringify({
+ transactionIDs: reconciledTransactions.map((x) => x.ID),
+ reconciliationTransactionAmount:
+ reconciliationTransactionAmount.toString(),
+ })
+ );
const response = await result.json();
const recTrans = response.ReconciliationTransaction;
if (recTrans) {
@@ -86,20 +92,19 @@ export const useTransactionsStore = defineStore("budget/transactions", {
}
},
logout() {
- this.$reset()
+ this.$reset();
},
async saveTransaction(payload: string) {
- const accountsStore = useAccountStore()
+ const accountsStore = useAccountStore();
const result = await POST("/transaction/new", payload);
- const response = await result.json() as Transaction;
+ const response = (await result.json()) as Transaction;
this.AddTransactions([response]);
accountsStore.CurrentAccount?.Transactions.unshift(response.ID);
},
async editTransaction(transactionid: string, payload: string) {
const result = await POST("/transaction/" + transactionid, payload);
- const response = await result.json() as Transaction;
+ const response = (await result.json()) as Transaction;
this.AddTransactions([response]);
- }
- }
-
-})
+ },
+ },
+});
diff --git a/web/tailwind.config.js b/web/tailwind.config.js
index 8b26c47..86a3ee2 100644
--- a/web/tailwind.config.js
+++ b/web/tailwind.config.js
@@ -1,10 +1,7 @@
module.exports = {
- content: [
- "./index.html",
- "./src/**/*.{vue,js,ts,jsx,tsx}"
- ],
- theme: {
- extend: {},
- },
- plugins: [],
-}
+ content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
+ theme: {
+ extend: {},
+ },
+ plugins: [],
+};
diff --git a/web/tsconfig.json b/web/tsconfig.json
index b4309f4..552e973 100644
--- a/web/tsconfig.json
+++ b/web/tsconfig.json
@@ -1,10 +1,10 @@
{
- "compilerOptions": {
- "target": "esnext",
- "module": "esnext",
- // this enables stricter inference for data properties on `this`
- "strict": true,
- "jsx": "preserve",
- "moduleResolution": "node"
- }
-}
\ No newline at end of file
+ "compilerOptions": {
+ "target": "esnext",
+ "module": "esnext",
+ // this enables stricter inference for data properties on `this`
+ "strict": true,
+ "jsx": "preserve",
+ "moduleResolution": "node"
+ }
+}
diff --git a/web/vite.config.js b/web/vite.config.js
index 8e1b264..ca5283a 100644
--- a/web/vite.config.js
+++ b/web/vite.config.js
@@ -1,30 +1,30 @@
-import { defineConfig } from 'vite'
-import vue from '@vitejs/plugin-vue'
+import { defineConfig } from "vite";
+import vue from "@vitejs/plugin-vue";
-import path from 'path'
+import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [
- vue(),
- // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin
- ],
- define: { 'process.env': {} },
- resolve: {
- alias: {
- '@': path.resolve(__dirname, 'src'),
+ plugins: [
+ vue(),
+ // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin
+ ],
+ define: { "process.env": {} },
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "src"),
+ },
},
- },
- server: {
- host: '0.0.0.0',
- proxy: {
- '/api': {
- target: 'http://10.0.0.162:1323/',
- changeOrigin: true
- }
- }
- }
- /* remove the need to specify .vue files https://vitejs.dev/config/#resolve-extensions
+ server: {
+ host: "0.0.0.0",
+ proxy: {
+ "/api": {
+ target: "http://10.0.0.162:1323/",
+ changeOrigin: true,
+ },
+ },
+ },
+ /* remove the need to specify .vue files https://vitejs.dev/config/#resolve-extensions
resolve: {
extensions: [
'.js',
@@ -37,4 +37,4 @@ export default defineConfig({
]
},
*/
-})
+});
diff --git a/web/yarn.lock b/web/yarn.lock
index f16a011..ee19f64 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -957,6 +957,21 @@
resolved "https://registry.yarnpkg.com/@emmetio/scanner/-/scanner-1.0.0.tgz#065b2af6233fe7474d44823e3deb89724af42b5f"
integrity sha512-8HqW8EVqjnCmWXVpqAOZf+EGESdkR27odcMMMGefgKXtar00SoYNSryGv//TELI4T3QFsECo78p+0lmalk/CFA==
+"@eslint/eslintrc@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.0.tgz#7ce1547a5c46dfe56e1e45c3c9ed18038c721c6a"
+ integrity sha512-igm9SjJHNEJRiUnecP/1R5T3wKLEJ7pL6e2P+GUSfCd0dGjPYYZve08uzw8L2J8foVHFz+NGu12JxRcU2gGo6w==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.3.1"
+ globals "^13.9.0"
+ ignore "^4.0.6"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.0.4"
+ strip-json-comments "^3.1.1"
+
"@hapi/address@2.x.x":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
@@ -1001,6 +1016,20 @@
dependencies:
"@hapi/hoek" "^9.0.0"
+"@humanwhocodes/config-array@^0.9.2":
+ version "0.9.5"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7"
+ integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==
+ dependencies:
+ "@humanwhocodes/object-schema" "^1.2.1"
+ debug "^4.1.1"
+ minimatch "^3.0.4"
+
+"@humanwhocodes/object-schema@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+ integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
"@jridgewell/resolve-uri@^3.0.3":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.4.tgz#b876e3feefb9c8d3aa84014da28b5e52a0640d72"
@@ -1281,6 +1310,50 @@
dependencies:
"@types/node" "*"
+"@typescript-eslint/parser@^5.13.0":
+ version "5.13.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.13.0.tgz#0394ed8f2f849273c0bf4b811994d177112ced5c"
+ integrity sha512-GdrU4GvBE29tm2RqWOM0P5QfCtgCyN4hXICj/X9ibKED16136l9ZpoJvCL5pSKtmJzA+NRDzQ312wWMejCVVfg==
+ dependencies:
+ "@typescript-eslint/scope-manager" "5.13.0"
+ "@typescript-eslint/types" "5.13.0"
+ "@typescript-eslint/typescript-estree" "5.13.0"
+ debug "^4.3.2"
+
+"@typescript-eslint/scope-manager@5.13.0":
+ version "5.13.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.13.0.tgz#cf6aff61ca497cb19f0397eea8444a58f46156b6"
+ integrity sha512-T4N8UvKYDSfVYdmJq7g2IPJYCRzwtp74KyDZytkR4OL3NRupvswvmJQJ4CX5tDSurW2cvCc1Ia1qM7d0jpa7IA==
+ dependencies:
+ "@typescript-eslint/types" "5.13.0"
+ "@typescript-eslint/visitor-keys" "5.13.0"
+
+"@typescript-eslint/types@5.13.0":
+ version "5.13.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.13.0.tgz#da1de4ae905b1b9ff682cab0bed6b2e3be9c04e5"
+ integrity sha512-LmE/KO6DUy0nFY/OoQU0XelnmDt+V8lPQhh8MOVa7Y5k2gGRd6U9Kp3wAjhB4OHg57tUO0nOnwYQhRRyEAyOyg==
+
+"@typescript-eslint/typescript-estree@5.13.0":
+ version "5.13.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.13.0.tgz#b37c07b748ff030a3e93d87c842714e020b78141"
+ integrity sha512-Q9cQow0DeLjnp5DuEDjLZ6JIkwGx3oYZe+BfcNuw/POhtpcxMTy18Icl6BJqTSd+3ftsrfuVb7mNHRZf7xiaNA==
+ dependencies:
+ "@typescript-eslint/types" "5.13.0"
+ "@typescript-eslint/visitor-keys" "5.13.0"
+ debug "^4.3.2"
+ globby "^11.0.4"
+ is-glob "^4.0.3"
+ semver "^7.3.5"
+ tsutils "^3.21.0"
+
+"@typescript-eslint/visitor-keys@5.13.0":
+ version "5.13.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.13.0.tgz#f45ff55bcce16403b221ac9240fbeeae4764f0fd"
+ integrity sha512-HLKEAS/qA1V7d9EzcpLFykTePmOQqOFim8oCvhY3pZgQ8Hi38hYpHd9e5GN6nQBFQNecNhws5wkS9Y5XIO0s/g==
+ dependencies:
+ "@typescript-eslint/types" "5.13.0"
+ eslint-visitor-keys "^3.0.0"
+
"@vitejs/plugin-vue@^2.0.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-2.1.0.tgz#ddf5e0059f84f2ff649afc25ce5a59211e670542"
@@ -2089,6 +2162,11 @@ acorn-import-assertions@^1.7.6:
resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9"
integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
+acorn-jsx@^5.3.1:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
acorn-node@^1.6.1:
version "1.8.2"
resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
@@ -2118,7 +2196,7 @@ acorn@^7.0.0, acorn@^7.1.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-acorn@^8.0.4, acorn@^8.0.5, acorn@^8.4.1:
+acorn@^8.0.4, acorn@^8.0.5, acorn@^8.4.1, acorn@^8.7.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
@@ -2160,7 +2238,7 @@ ajv-keywords@^5.0.0:
dependencies:
fast-deep-equal "^3.1.3"
-ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
+ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
@@ -2277,6 +2355,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
arr-diff@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@@ -3351,7 +3434,7 @@ cross-spawn@^6.0.0:
shebang-command "^1.2.0"
which "^1.2.9"
-cross-spawn@^7.0.3:
+cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -3528,7 +3611,7 @@ debug@^3.1.1:
dependencies:
ms "^2.1.1"
-debug@^4.1.0, debug@^4.1.1:
+debug@^4.1.0, debug@^4.1.1, debug@^4.3.2:
version "4.3.3"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664"
integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==
@@ -3552,6 +3635,11 @@ deep-equal@^1.0.1:
object-keys "^1.1.1"
regexp.prototype.flags "^1.2.0"
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
deepmerge@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
@@ -3724,6 +3812,13 @@ dns-txt@^2.0.2:
dependencies:
buffer-indexof "^1.0.0"
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
doctypes@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9"
@@ -4046,6 +4141,21 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-plugin-vue@^8.5.0:
+ version "8.5.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-8.5.0.tgz#65832bba43ca713fa5da16bdfcf55d0095677f6f"
+ integrity sha512-i1uHCTAKOoEj12RDvdtONWrGzjFm/djkzqfhmQ0d6M/W8KM81mhswd/z+iTZ0jCpdUedW3YRgcVfQ37/J4zoYQ==
+ dependencies:
+ eslint-utils "^3.0.0"
+ natural-compare "^1.4.0"
+ semver "^7.3.5"
+ vue-eslint-parser "^8.0.1"
+
eslint-scope@5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
@@ -4062,11 +4172,93 @@ eslint-scope@^4.0.3:
esrecurse "^4.1.0"
estraverse "^4.1.1"
+eslint-scope@^7.0.0, eslint-scope@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
+ integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
+eslint-utils@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+ integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
+ dependencies:
+ eslint-visitor-keys "^2.0.0"
+
+eslint-visitor-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+ integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+ integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
+
+eslint@^8.10.0:
+ version "8.10.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.10.0.tgz#931be395eb60f900c01658b278e05b6dae47199d"
+ integrity sha512-tcI1D9lfVec+R4LE1mNDnzoJ/f71Kl/9Cv4nG47jOueCMBrCCKYXr4AUVS7go6mWYGFD4+EoN6+eXSrEbRzXVw==
+ dependencies:
+ "@eslint/eslintrc" "^1.2.0"
+ "@humanwhocodes/config-array" "^0.9.2"
+ ajv "^6.10.0"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.1.1"
+ eslint-utils "^3.0.0"
+ eslint-visitor-keys "^3.3.0"
+ espree "^9.3.1"
+ esquery "^1.4.0"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ functional-red-black-tree "^1.0.1"
+ glob-parent "^6.0.1"
+ globals "^13.6.0"
+ ignore "^5.2.0"
+ import-fresh "^3.0.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.0.4"
+ natural-compare "^1.4.0"
+ optionator "^0.9.1"
+ regexpp "^3.2.0"
+ strip-ansi "^6.0.1"
+ strip-json-comments "^3.1.0"
+ text-table "^0.2.0"
+ v8-compile-cache "^2.0.3"
+
+espree@^9.0.0, espree@^9.3.1:
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd"
+ integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==
+ dependencies:
+ acorn "^8.7.0"
+ acorn-jsx "^5.3.1"
+ eslint-visitor-keys "^3.3.0"
+
esprima@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+esquery@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+ integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
+ dependencies:
+ estraverse "^5.1.0"
+
esrecurse@^4.1.0, esrecurse@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
@@ -4079,7 +4271,7 @@ estraverse@^4.1.1:
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
-estraverse@^5.2.0:
+estraverse@^5.1.0, estraverse@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
@@ -4289,6 +4481,11 @@ fast-json-stable-stringify@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
fastq@^1.6.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
@@ -4315,6 +4512,13 @@ figures@^2.0.0:
dependencies:
escape-string-regexp "^1.0.5"
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
file-saver@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
@@ -4388,6 +4592,19 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
+flat-cache@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+ integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+ dependencies:
+ flatted "^3.1.0"
+ rimraf "^3.0.2"
+
+flatted@^3.1.0:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
+ integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
+
flush-write-stream@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
@@ -4529,6 +4746,11 @@ function-bind@^1.1.1:
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+functional-red-black-tree@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+ integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+
gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -4626,7 +4848,14 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
-globby@^11.0.1, globby@^11.0.2, globby@^11.0.3:
+globals@^13.6.0, globals@^13.9.0:
+ version "13.12.1"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.1.tgz#ec206be932e6c77236677127577aa8e50bf1c5cb"
+ integrity sha512-317dFlgY2pdJZ9rspXDks7073GpDmXdfbM3vYYp0HAMKGDh1FfWPleI2ljVNLQX5M5lXcAslTcPTrOrMEFOjyw==
+ dependencies:
+ type-fest "^0.20.2"
+
+globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.0.4:
version "11.1.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
@@ -4961,7 +5190,7 @@ iferr@^0.1.5:
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
-ignore@^4.0.3:
+ignore@^4.0.3, ignore@^4.0.6:
version "4.0.6"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
@@ -4976,7 +5205,7 @@ immutable@^4.0.0:
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==
-import-fresh@^3.1.0, import-fresh@^3.2.1:
+import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
version "3.3.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
@@ -5373,6 +5602,13 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -5413,6 +5649,11 @@ json-schema@0.4.0:
resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5"
integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+
json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -5513,6 +5754,14 @@ launch-editor@^2.2.1, launch-editor@^2.3.0:
picocolors "^1.0.0"
shell-quote "^1.6.1"
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
lilconfig@^2.0.3, lilconfig@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.4.tgz#f4507d043d7058b380b6a8f5cb7bcd4b34cee082"
@@ -5591,6 +5840,11 @@ lodash.memoize@^4.1.2:
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@@ -5979,6 +6233,11 @@ nanomatch@^1.2.9:
snapdragon "^0.8.1"
to-regex "^3.0.1"
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+
negotiator@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
@@ -6235,6 +6494,18 @@ opener@^1.5.2:
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==
+optionator@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+ integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+ dependencies:
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+ word-wrap "^1.2.3"
+
ora@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318"
@@ -6817,7 +7088,12 @@ postcss@^8.1.10, postcss@^8.2.15, postcss@^8.2.6, postcss@^8.3.5, postcss@^8.4.5
picocolors "^1.0.0"
source-map-js "^1.0.2"
-"prettier@^1.18.2 || ^2.0.0":
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+prettier@2.5.1, "prettier@^1.18.2 || ^2.0.0":
version "2.5.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a"
integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==
@@ -7196,6 +7472,11 @@ regexp.prototype.flags@^1.2.0:
call-bind "^1.0.2"
define-properties "^1.1.3"
+regexpp@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+ integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
+
regexpu-core@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3"
@@ -7975,6 +8256,11 @@ strip-indent@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
stylehacks@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.0.2.tgz#fa10e5181c6e8dc0bddb4a3fb372e9ac42bba2ad"
@@ -8112,6 +8398,11 @@ terser@^5.10.0, terser@^5.7.2:
source-map "~0.7.2"
source-map-support "~0.5.20"
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
+
thenify-all@^1.0.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
@@ -8288,6 +8579,13 @@ tsutils@^2.29.0:
dependencies:
tslib "^1.8.1"
+tsutils@^3.21.0:
+ version "3.21.0"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
+ integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
+ dependencies:
+ tslib "^1.8.1"
+
tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@@ -8305,6 +8603,18 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
type-fest@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
@@ -8472,6 +8782,11 @@ uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+v8-compile-cache@^2.0.3:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+ integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
validate-npm-package-license@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@@ -8644,6 +8959,19 @@ vue-demi@*:
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.12.1.tgz#f7e18efbecffd11ab069d1472d7a06e319b4174c"
integrity sha512-QL3ny+wX8c6Xm1/EZylbgzdoDolye+VpCXRhI2hug9dJTP3OUJ3lmiKN3CsVV3mOJKwFi0nsstbgob0vG7aoIw==
+vue-eslint-parser@^8.0.1:
+ version "8.3.0"
+ resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz#5d31129a1b3dd89c0069ca0a1c88f970c360bd0d"
+ integrity sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==
+ dependencies:
+ debug "^4.3.2"
+ eslint-scope "^7.0.0"
+ eslint-visitor-keys "^3.1.0"
+ espree "^9.0.0"
+ esquery "^1.4.0"
+ lodash "^4.17.21"
+ semver "^7.3.5"
+
vue-hot-reload-api@^2.3.0:
version "2.3.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"
@@ -8953,6 +9281,11 @@ with@^7.0.0:
assert-never "^1.2.1"
babel-walk "3.0.0-canary-5"
+word-wrap@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
worker-farm@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8"