-
{
- console.log(transactionPoints)
- }}>Console Log Transactions
-
- {balance}
diff --git a/src/pages/dashboard/GraphUtils.ts b/src/pages/dashboard/GraphUtils.ts
deleted file mode 100644
index 266777c..0000000
--- a/src/pages/dashboard/GraphUtils.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import {Transaction} from "../../utils/transaction.ts";
-
-type transactionPoint = { date: string; amount: number }
-
-// const cumulateTransactions = (points: transactionPoint[]): transactionPoint[] => {
-// let total = 0;
-// return points.map(value => {
-// total += value.amount;
-// return {date: value.date, amount: total};
-// })
-// }
-
-const getDateString = (timestamp: number): string => {
- const date = new Date(timestamp)
- const day = date.getDate().toString().padStart(2, '0'); // Ensures two digits
- const month = (date.getMonth() + 1).toString().padStart(2, '0'); // Month is 0-indexed, add 1
- const year = date.getFullYear();
- return `${day}/${month}/${year}`;
-}
-export const splitTransactions = (data: transactionPoint[]): void => {
- const moneyIn: transactionPoint[] = []
- const moneyOut: transactionPoint[] = []
- data.forEach(t => {
- if (t.amount > 0) {
- moneyIn.push(t)
- } else {
- moneyOut.push(t)
- }
- })
-}
-export const readTransactions = (data: Transaction[]): void => {
- const result: transactionPoint[] = []
- data.forEach(t => {
- result.push({amount: t.amount, date: getDateString(t.dateTime)})
- })
- splitTransactions(result)
-}
\ No newline at end of file
diff --git a/src/pages/dashboard/TileUtils.ts b/src/pages/dashboard/TileUtils.ts
new file mode 100644
index 0000000..a18e05f
--- /dev/null
+++ b/src/pages/dashboard/TileUtils.ts
@@ -0,0 +1,43 @@
+import {ReactNode} from "react";
+import {transactionPoint} from "./graphs/GraphUtils.ts";
+import {min} from "lodash";
+
+type tsxContents = ReactNode;
+
+export class TileElement {
+ private readonly graph?: transactionPoint[];
+ private readonly TSX?: () => tsxContents;
+ public readonly cols: number;
+ public readonly rows: number;
+
+ private constructor(graph: transactionPoint[] | undefined, TSX: (() => tsxContents) | undefined, cols: number, rows: number) {
+ this.graph = graph;
+ this.TSX = TSX;
+ this.cols = cols;
+ this.rows = rows;
+ }
+
+ static newGraph(graph: transactionPoint[], cols: number, rows: number, maxCol: number): TileElement {
+ return new TileElement(graph, undefined, min([cols, maxCol])!, rows);
+ }
+ static newTSX(TSX: () => tsxContents, cols: number, rows: number, maxCol: number): TileElement {
+ return new TileElement(undefined, TSX, min([cols, maxCol])!, rows);
+ }
+
+ isGraph(): boolean {
+ return typeof this.graph !== "undefined";
+ }
+
+ forceGetGraph(): transactionPoint[] {
+ return this.graph!;
+ }
+ forceGetTSX(): () => tsxContents {
+ return this.TSX!;
+ }
+}
+
+export type tileData = { d: TileElement; rows: number; cols: number };
+
+export function getTileSize (tile: TileElement) {
+ return {colSpan: tile.cols, rowSpan: tile.rows};
+}
\ No newline at end of file
diff --git a/src/pages/dashboard/goals tile/GoalsTile.css b/src/pages/dashboard/goals tile/GoalsTile.css
new file mode 100644
index 0000000..27ca49e
--- /dev/null
+++ b/src/pages/dashboard/goals tile/GoalsTile.css
@@ -0,0 +1,227 @@
+.modified-multi-range-slider * {
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
+
+.modified-multi-range-slider {
+ display: flex;
+ position: relative;
+ padding: 20px 10px;
+ flex-direction: column;
+ -webkit-touch-callout: none; /* iOS Safari */
+ -webkit-user-select: none; /* Safari */
+ -khtml-user-select: none; /* Konqueror HTML */
+ -moz-user-select: none; /* Old versions of Firefox */
+ -ms-user-select: none; /* Internet Explorer/Edge */
+ user-select: none; /* Non-prefixed version, currently supported by Chrome, Edge,*/
+}
+
+.modified-multi-range-slider .bar {
+ display: flex;
+}
+
+.modified-multi-range-slider .bar-left {
+ width: 25%;
+ background-color: rgb(202, 15, 15);
+ border-radius: 10px 0 0 10px;
+ padding: 4px 0;
+}
+
+.modified-multi-range-slider .bar-right {
+ width: 25%;
+ background-color: green;
+ border-radius: 0 10px 10px 0;
+}
+
+.modified-multi-range-slider .bar-inner {
+ background-color: #0000bf;
+ display: flex;
+ flex-grow: 1;
+ flex-shrink: 1;
+ justify-content: space-between;
+ position: relative;
+}
+
+.modified-multi-range-slider .bar-inner-left {
+ width: 50%;
+}
+
+.modified-multi-range-slider .bar-inner-right {
+ width: 50%;
+}
+
+.modified-multi-range-slider .thumb {
+ background-color: #d6cfcf;
+ position: relative;
+ z-index: 1;
+ cursor: pointer;
+}
+
+.modified-multi-range-slider .thumb::before {
+ content: "";
+ background-color: inherit;
+ position: absolute;
+ top: 3px;
+ width: 17px;
+ height: 17px;
+ border: solid 1px #979797;
+ border-radius: 50%;
+ z-index: 1;
+ margin: -8px -12px;
+ cursor: pointer;
+}
+
+.modified-multi-range-slider .input-type-range:focus + .thumb::after {
+ content: "";
+ position: absolute;
+ top: -4px;
+ left: -7px;
+ width: 11px;
+ height: 11px;
+ z-index: 2;
+ border-radius: 50%;
+ border: dotted 1px black;
+ box-shadow: 0 0 5px white, inset 0 0 10px black;
+}
+
+.modified-multi-range-slider .caption {
+ display: none;
+}
+
+.modified-multi-range-slider .thumb .caption * {
+ display: none;
+}
+
+.modified-multi-range-slider .thumb:active .caption {
+ display: none;
+}
+
+.modified-multi-range-slider .input-type-range:focus + .thumb .caption {
+ display: flex;
+}
+
+.modified-multi-range-slider .input-type-range {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ opacity: 0;
+ pointer-events: none;
+}
+
+.modified-multi-range-slider .ruler {
+ margin: 10px 0 -5px 0;
+ display: flex;
+ /* display: none; */
+ overflow: hidden;
+}
+
+.modified-multi-range-slider .ruler .ruler-rule {
+ border-left: solid 1px;
+ border-bottom: solid 1px;
+ display: flex;
+ flex-grow: 1;
+ flex-shrink: 1;
+ padding: 5px 0;
+}
+
+.modified-multi-range-slider .ruler .ruler-rule:last-child {
+ border-right: solid 1px;
+}
+
+.modified-multi-range-slider .ruler .ruler-sub-rule {
+ border-left: solid 1px;
+ /* border-bottom: solid 1px; */
+ display: flex;
+ flex-grow: 1;
+ flex-shrink: 1;
+ padding: 3px 0;
+ bottom: 0;
+ margin-bottom: -5px;
+}
+
+.modified-multi-range-slider .ruler .ruler-sub-rule:first-child {
+ border-left: none;
+}
+
+.modified-multi-range-slider .labels {
+ display: flex;
+ justify-content: space-between;
+ padding: 0;
+ margin-top: 10px;
+ margin-bottom: -20px;
+ /* display: none; */
+}
+
+.modified-multi-range-slider .label {
+ font-size: 80%;
+ display: flex;
+ width: 1px;
+ justify-content: center;
+}
+
+.modified-multi-range-slider .label:first-child {
+ justify-content: start;
+}
+
+.modified-multi-range-slider .label:last-child {
+ justify-content: end;
+}
+
+.modified-multi-range-slider.zero-ranage-margin .thumb-left {
+ right: 12px;
+}
+
+.modified-multi-range-slider.zero-ranage-margin .thumb-right {
+ left: 8px;
+}
+
+/* Disabled */
+.modified-multi-range-slider.disabled {
+ border: solid 1px rgb(200, 200, 200);
+ box-shadow: 1px 1px 4px rgb(180, 180, 180);
+ color: rgb(180, 180, 180);
+}
+
+.modified-multi-range-slider .bar {
+ display: flex;
+}
+
+.modified-multi-range-slider.disabled .bar-left {
+ background-color: #c9c9c9;
+ box-shadow: inset 0 0 5px rgb(160, 160, 160);
+}
+
+.modified-multi-range-slider.disabled .bar-right {
+ background-color: #c9c9c9;
+ box-shadow: inset 0 0 5px rgb(160, 160, 160);
+}
+
+.modified-multi-range-slider.disabled .bar-inner {
+ background-color: rgb(130, 243, 130);
+ border: solid 1px rgb(149, 149, 149);
+ box-shadow: inset 0 0 5px rgb(103, 103, 103);
+}
+
+.modified-multi-range-slider.disabled .thumb {
+ background-color: white;
+}
+
+.modified-multi-range-slider.disabled .thumb::before {
+ border: solid 1px rgb(200, 200, 200);
+ box-shadow: 0 0 3px rgb(35, 35, 35), inset 0 0 5px gray;
+}
+
+.modified-multi-range-slider.disabled .input-type-range:focus + .thumb::after {
+ border: dotted 1px rgb(35, 35, 35);
+ box-shadow: 0 0 5px white, inset 0 0 10px rgb(35, 35, 35);
+}
+
+.modified-multi-range-slider.disabled .thumb .caption * {
+ background-color: rgb(84, 84, 137);
+ color: rgb(199, 199, 199);
+ box-shadow: 0 0 5px rgb(35, 35, 35);
+}
+
+/*# sourceMappingURL=GoalsTile.css.map */
diff --git a/src/pages/dashboard/goals tile/GoalsTile.css.map b/src/pages/dashboard/goals tile/GoalsTile.css.map
new file mode 100644
index 0000000..203b8eb
--- /dev/null
+++ b/src/pages/dashboard/goals tile/GoalsTile.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["GoalsTile.scss"],"names":[],"mappings":"AAAA;EACE;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;AACA;EACA;;;AAEF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAGF;EACE;AACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEF;EACE;;;AAGF;EACE;EACA;EACA;EACA;EACA;AACA;;;AAEF;EACE;EACA;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAEF;EACE;;;AAIF;AACA;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;EACA;;;AAEF;EACE;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;;;AAEF;EACE;EACA;EACA","file":"GoalsTile.css"}
\ No newline at end of file
diff --git a/src/pages/dashboard/goals tile/GoalsTile.scss b/src/pages/dashboard/goals tile/GoalsTile.scss
new file mode 100644
index 0000000..b05c5a5
--- /dev/null
+++ b/src/pages/dashboard/goals tile/GoalsTile.scss
@@ -0,0 +1,199 @@
+.modified-multi-range-slider * {
+ box-sizing: border-box;
+ padding: 0;
+ margin: 0;
+}
+.modified-multi-range-slider {
+ display: flex;
+ position: relative;
+ padding: 20px 10px;
+ flex-direction: column;
+ -webkit-touch-callout: none; /* iOS Safari */
+ -webkit-user-select: none; /* Safari */
+ -khtml-user-select: none; /* Konqueror HTML */
+ -moz-user-select: none; /* Old versions of Firefox */
+ -ms-user-select: none; /* Internet Explorer/Edge */
+ user-select: none; /* Non-prefixed version, currently supported by Chrome, Edge,*/
+}
+.modified-multi-range-slider .bar {
+ display: flex;
+}
+.modified-multi-range-slider .bar-left {
+ width: 25%;
+ background-color: rgb(202, 15, 15);
+ border-radius: 10px 0 0 10px;
+ padding: 4px 0;
+}
+.modified-multi-range-slider .bar-right {
+ width: 25%;
+ background-color: green;
+ border-radius: 0 10px 10px 0;
+}
+.modified-multi-range-slider .bar-inner {
+ background-color: #0000bf;
+ display: flex;
+ flex-grow: 1;
+ flex-shrink: 1;
+ justify-content: space-between;
+ position: relative;
+}
+.modified-multi-range-slider .bar-inner-left {
+ width: 50%;
+}
+.modified-multi-range-slider .bar-inner-right {
+ width: 50%;
+}
+.modified-multi-range-slider .thumb {
+ background-color: #d6cfcf;
+ position: relative;
+ z-index: 1;
+ cursor: pointer;
+}
+
+.modified-multi-range-slider .thumb::before {
+ content: '';
+ background-color: inherit;
+ position: absolute;
+ top: 3px;
+ width: 17px;
+ height: 17px;
+ border: solid 1px #979797;
+ border-radius: 50%;
+ z-index: 1;
+ margin: -8px -12px;
+ cursor: pointer;
+}
+.modified-multi-range-slider .input-type-range:focus + .thumb::after {
+ content: '';
+ position: absolute;
+ top: -4px;
+ left: -7px;
+ width: 11px;
+ height: 11px;
+ z-index: 2;
+ border-radius: 50%;
+ border: dotted 1px black;
+ box-shadow: 0 0 5px white, inset 0 0 10px black;
+}
+.modified-multi-range-slider .caption {
+ display: none;
+}
+.modified-multi-range-slider .thumb .caption * {
+ display: none;
+}
+.modified-multi-range-slider .thumb:active .caption {
+ display: none;
+}
+.modified-multi-range-slider .input-type-range:focus + .thumb .caption {
+ display: flex;
+}
+
+.modified-multi-range-slider .input-type-range {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ opacity: 0;
+ pointer-events: none;
+}
+
+.modified-multi-range-slider .ruler {
+ margin: 10px 0 -5px 0;
+ display: flex;
+ /* display: none; */
+ overflow: hidden;
+}
+.modified-multi-range-slider .ruler .ruler-rule {
+ border-left: solid 1px;
+ border-bottom: solid 1px;
+ display: flex;
+ flex-grow: 1;
+ flex-shrink: 1;
+ padding: 5px 0;
+}
+.modified-multi-range-slider .ruler .ruler-rule:last-child {
+ border-right: solid 1px;
+}
+
+.modified-multi-range-slider .ruler .ruler-sub-rule {
+ border-left: solid 1px;
+ /* border-bottom: solid 1px; */
+ display: flex;
+ flex-grow: 1;
+ flex-shrink: 1;
+ padding: 3px 0;
+ bottom: 0;
+ margin-bottom: -5px;
+}
+.modified-multi-range-slider .ruler .ruler-sub-rule:first-child {
+ border-left: none;
+}
+
+.modified-multi-range-slider .labels {
+ display: flex;
+ justify-content: space-between;
+ padding: 0;
+ margin-top: 10px;
+ margin-bottom: -20px;
+ /* display: none; */
+}
+.modified-multi-range-slider .label {
+ font-size: 80%;
+ display: flex;
+ width: 1px;
+ justify-content: center;
+}
+.modified-multi-range-slider .label:first-child {
+ justify-content: start;
+}
+.modified-multi-range-slider .label:last-child {
+ justify-content: end;
+}
+.modified-multi-range-slider.zero-ranage-margin .thumb-left {
+ right: 12px;
+}
+.modified-multi-range-slider.zero-ranage-margin .thumb-right {
+ left: 8px;
+}
+
+
+/* Disabled */
+.modified-multi-range-slider.disabled {
+ border: solid 1px rgb(200, 200, 200);
+ box-shadow: 1px 1px 4px rgb(180, 180, 180);
+ color:rgb(180, 180, 180);
+}
+.modified-multi-range-slider .bar {
+ display: flex;
+}
+.modified-multi-range-slider.disabled .bar-left {
+ background-color: #c9c9c9;
+ box-shadow: inset 0 0 5px rgb(160, 160, 160);
+}
+.modified-multi-range-slider.disabled .bar-right {
+ background-color: #c9c9c9;
+ box-shadow: inset 0 0 5px rgb(160, 160, 160);
+}
+.modified-multi-range-slider.disabled .bar-inner {
+ background-color: rgb(130 243 130);
+ border: solid 1px rgb(149, 149, 149);
+ box-shadow: inset 0 0 5px rgb(103, 103, 103);
+}
+.modified-multi-range-slider.disabled .thumb {
+ background-color: white;
+}
+.modified-multi-range-slider.disabled .thumb::before {
+ border: solid 1px rgb(200, 200, 200);
+ box-shadow: 0 0 3px rgb(35, 35, 35), inset 0 0 5px gray;
+}
+.modified-multi-range-slider.disabled .input-type-range:focus + .thumb::after {
+ border: dotted 1px rgb(35, 35, 35);
+ box-shadow: 0 0 5px white, inset 0 0 10px rgb(35, 35, 35);
+}
+.modified-multi-range-slider.disabled .thumb .caption * {
+ background-color: rgb(84, 84, 137);
+ color: rgb(199, 199, 199);
+ box-shadow: 0 0 5px rgb(35, 35, 35);
+}
+
+
diff --git a/src/pages/dashboard/goals tile/GoalsTile.tsx b/src/pages/dashboard/goals tile/GoalsTile.tsx
new file mode 100644
index 0000000..313b5b9
--- /dev/null
+++ b/src/pages/dashboard/goals tile/GoalsTile.tsx
@@ -0,0 +1,107 @@
+import React, {ReactNode, useState} from "react";
+import {setUserPrefs, UserPrefs} from "../../../utils/user_prefs.ts";
+import MultiRangeSlider, {ChangeResult} from "multi-range-slider-react";
+import "./GoalsTile.scss";
+import {auth} from "../../../utils/firebase.ts";
+
+export default function goalsTile(userPrefs: UserPrefs, forceUpdate: () => void): ReactNode {
+ const [minValue, setMinValue] = useState(
+ Math.round(userPrefs.getNeedsBudget() * 100)
+ );
+ const [maxValue, setMaxValue] = useState(
+ Math.round((userPrefs.getNeedsBudget() + userPrefs.getWantsBudget()) * 100)
+ );
+
+ const needs = minValue;
+ const wants = maxValue - minValue;
+ const savings = 100 - maxValue;
+
+ const disableSet = needs === Math.round(userPrefs.getNeedsBudget() * 100) && wants === Math.round(userPrefs.getWantsBudget() * 100);
+ const disableRecommended = needs === 50 && wants === 30;
+
+ const handleInput = (e: ChangeResult) => {
+ const min = e.minValue;
+ const max = e.maxValue;
+
+ setMinValue(min);
+ setMaxValue(max);
+ };
+
+ const stop = (e: React.PointerEvent
) => {
+ e.stopPropagation();
+ e.nativeEvent.stopImmediatePropagation();
+ }
+
+ return <>
+
+ Goals
+
+
+
+
+ {
+ await setUserPrefs(auth.currentUser!, UserPrefs.newChecked(needs / 100, wants / 100));
+ forceUpdate();
+ }}
+ >
+ Set
+
+ {
+ setMinValue(50);
+ setMaxValue(80);
+ }}
+ >
+ Recommended
+
+
+
+ >;
+}
\ No newline at end of file
diff --git a/src/pages/dashboard/graphs/GraphUtils.ts b/src/pages/dashboard/graphs/GraphUtils.ts
new file mode 100644
index 0000000..dab63e1
--- /dev/null
+++ b/src/pages/dashboard/graphs/GraphUtils.ts
@@ -0,0 +1,39 @@
+import {Transaction} from "../../../utils/transaction.ts";
+import strftime from "strftime";
+
+export type transactionPoint = { date: string; amount: number; goal: number }
+export type finalGraphData = {raw: transactionPoint[], in: transactionPoint[], out: transactionPoint[]};
+
+function cumulateTransactions(points: transactionPoint[]): transactionPoint[] {
+ let total = 0;
+ return points.map(value => {
+ total += value.amount;
+ value.amount = total;
+ return value;
+ })
+}
+
+function getDateString(timestamp: number): string {
+ return strftime("%d/%m/%y", new Date(timestamp))
+}
+
+function splitTransactions (data: transactionPoint[]): finalGraphData {
+ const moneyIn: transactionPoint[] = []
+ const moneyOut: transactionPoint[] = []
+ data.forEach(t => {
+ if (t.amount > 0) {
+ moneyIn.push(t)
+ } else {
+ moneyOut.push(t)
+ }
+ })
+ return {raw: cumulateTransactions(data), in: cumulateTransactions(moneyIn), out: cumulateTransactions(moneyOut)};
+}
+
+export function readTransactions(data: Transaction[]): finalGraphData {
+ return splitTransactions(
+ data.map((t) => {
+ return {date: getDateString(t.dateTime), amount: t.amount, goal: 800};
+ })
+ );
+}
\ No newline at end of file
diff --git a/src/pages/dashboard/Graphs.tsx b/src/pages/dashboard/graphs/Graphs.tsx
similarity index 81%
rename from src/pages/dashboard/Graphs.tsx
rename to src/pages/dashboard/graphs/Graphs.tsx
index 8bf93e7..098d6c8 100644
--- a/src/pages/dashboard/Graphs.tsx
+++ b/src/pages/dashboard/graphs/Graphs.tsx
@@ -1,6 +1,5 @@
import {Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis} from "recharts";
-
-type transactionPoint = { date: string; amount: number }
+import {transactionPoint} from "./GraphUtils.ts";
interface Props {
data: transactionPoint[];
@@ -14,6 +13,7 @@ export default function Graphs({data}: Props) {
+
);
diff --git a/src/pages/dashboard/test.tsx b/src/pages/dashboard/test.tsx
deleted file mode 100644
index 13cba03..0000000
--- a/src/pages/dashboard/test.tsx
+++ /dev/null
@@ -1,7 +0,0 @@
-export default function test() {
- return (
- <>
- Hi
- >
- );
-}
\ No newline at end of file
diff --git a/src/pages/dashboard/total tile/TotalTile.tsx b/src/pages/dashboard/total tile/TotalTile.tsx
new file mode 100644
index 0000000..3507508
--- /dev/null
+++ b/src/pages/dashboard/total tile/TotalTile.tsx
@@ -0,0 +1,38 @@
+import {ReactNode} from "react";
+import { Transaction } from "../../../utils/transaction.ts";
+import {max, min} from "lodash";
+
+export default function totalTile(transactions: Transaction[]): ReactNode {
+ const balance = transactions.reduce((prev, curr): number => prev + curr.amount, 0);
+ const income = transactions.reduce((prev, curr): number => prev + max([curr.amount, 0])!, 0);
+ const expenses = transactions.reduce((prev, curr): number => prev + min([curr.amount, 0])!, 0);
+
+ return
+
+
+
Balance:
+ {balance > 0 ? <>
+
£
+
{balance.toFixed(2)}
+ > : <>
+
£
+
{balance.toFixed(2)}
+ >}
+
+
+
+
+
Income:
+
£
+
{income.toFixed(2)}
+
+
+
+
+
Expenses:
+
£
+
{expenses.toFixed(2)}
+
+
+ ;
+}
\ No newline at end of file
diff --git a/src/pages/test firestore/TestFirestore.tsx b/src/pages/test firestore/TestFirestore.tsx
index 2655ca2..647c59d 100644
--- a/src/pages/test firestore/TestFirestore.tsx
+++ b/src/pages/test firestore/TestFirestore.tsx
@@ -15,6 +15,7 @@ import {Header} from "../../components/Header.tsx";
import { orderBy } from "firebase/firestore";
import {User} from "firebase/auth";
import {getUserPrefs, setUserPrefs, UserPrefs} from "../../utils/user_prefs.ts";
+import {round} from "lodash";
function writeSampleData() {
if (auth.currentUser === null) {
@@ -95,11 +96,12 @@ export function TestFirestorePage() {
UserPrefs
{
userPrefs ? <>
- Goal: {userPrefs.goal}
+ Goal: {userPrefs.getNeedsBudget()} | {userPrefs.getWantsBudget()} | {userPrefs.getSavingsBudget()}
{
- userPrefs!.goal += 100;
- setUserPrefs(auth.currentUser!, userPrefs).then(() => setUpdate(update + 1));
- }}>Increment
+ const needs = round(faker.number.float({min: 0, max: 0.5}), 2);
+ const wants = round(faker.number.float({min: 0, max: 0.5}), 2);
+ setUserPrefs(auth.currentUser!, UserPrefs.newChecked(needs, wants)).then(() => setUpdate(update + 1));
+ }}>Randomise
> : Loading
}
diff --git a/src/router.tsx b/src/router.tsx
index 9b24d6f..f5b2c09 100644
--- a/src/router.tsx
+++ b/src/router.tsx
@@ -26,7 +26,7 @@ export const router = createBrowserRouter([
element: ,
},
{
- path: "/user-test",
+ path: "/user-tiles",
element: ,
errorElement:<_404Page/>
},
@@ -47,7 +47,7 @@ export const router = createBrowserRouter([
element: ,
},
{
- path: "/test",
+ path: "/tiles",
element: ,
},
]);
\ No newline at end of file
diff --git a/src/utils/user_prefs.test.ts b/src/utils/user_prefs.test.ts
index 6ab94d1..5651651 100644
--- a/src/utils/user_prefs.test.ts
+++ b/src/utils/user_prefs.test.ts
@@ -14,7 +14,7 @@ describe("Firestore UserPrefs Tests", () => {
expect(_.isEqual(prefs, UserPrefs.default()), "Non-existent UserPrefs should return default").toBeTruthy();
- prefs.goal = 213;
+ const new_prefs = UserPrefs.newChecked(0.4, 0.2);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
@@ -24,7 +24,7 @@ describe("Firestore UserPrefs Tests", () => {
// @ts-expect-error
const read_prefs = await getUserPrefs(user);
- console.log(prefs);
+ console.log(new_prefs);
console.log(read_prefs);
expect(_.isEqual(read_prefs, prefs), "UserPref changes to be read").toBeTruthy();
diff --git a/src/utils/user_prefs.ts b/src/utils/user_prefs.ts
index 2aa65b5..fc822c4 100644
--- a/src/utils/user_prefs.ts
+++ b/src/utils/user_prefs.ts
@@ -1,17 +1,52 @@
import {collection, doc, DocumentSnapshot, getDoc, setDoc, SnapshotOptions} from "firebase/firestore";
import {User} from "firebase/auth";
import {db} from "./firebase.ts";
+import {round} from "lodash";
export class UserPrefs {
- public goal: number;
+ private readonly needsBudget: number;
+ private readonly wantsBudget: number;
- constructor(goal: number) {
- this.goal = goal;
+ private constructor(needsBudget: number, wantsBudget: number) {
+ if (needsBudget > 1) {
+ needsBudget = 1;
+ }
+
+ if (needsBudget + wantsBudget > 1) {
+ wantsBudget = 1 - needsBudget;
+ }
+
+ this.needsBudget = round(needsBudget, 2);
+ this.wantsBudget = round(wantsBudget, 2);
+ }
+
+ static newChecked(needsBudget: number, wantsBudget: number): UserPrefs {
+ if (needsBudget > 1) {
+ throw new Error("needsBudget > 1!");
+ }
+
+ if (needsBudget + wantsBudget > 1) {
+ throw new Error("needsBudget + wantsBudget > 1!");
+ }
+
+ return new UserPrefs(needsBudget, wantsBudget);
}
static default(): UserPrefs {
- return new UserPrefs(100);
+ return new UserPrefs(0.5, 0.3);
+ }
+
+ getNeedsBudget(): number {
+ return this.needsBudget;
+ }
+
+ getWantsBudget(): number {
+ return this.wantsBudget;
+ }
+
+ getSavingsBudget(): number {
+ return round(1 - this.wantsBudget - this.needsBudget, 2);
}
// Utility method for creating `Transactions`
@@ -20,7 +55,8 @@ export class UserPrefs {
if (!data) {
throw Error("No data returned for snapshot!");
}
- return new UserPrefs(data.goal);
+
+ return new UserPrefs(round(data.needsBudget, 2), round(data.wantsBudget, 2));
}
toSendObject(): object {