diff options
Diffstat (limited to 'gui')
| -rw-r--r-- | gui/src/ajax/backend.ts | 8 | ||||
| -rw-r--r-- | gui/src/components/BudgetUpdatesTable/BudgetUpdatesTable.css (renamed from gui/src/components/app/App.css) | 0 | ||||
| -rw-r--r-- | gui/src/components/BudgetUpdatesTable/BudgetUpdatesTable.tsx | 36 | ||||
| -rw-r--r-- | gui/src/components/histogram/Histogram.tsx | 32 | ||||
| -rw-r--r-- | gui/src/index.tsx | 2 | ||||
| -rw-r--r-- | gui/src/types/budgetUpdate.ts | 31 | ||||
| -rw-r--r-- | gui/src/types/category.ts | 3 | ||||
| -rw-r--r-- | gui/src/types/group.ts | 8 | ||||
| -rw-r--r-- | gui/src/util/groupCategories.ts | 12 | ||||
| -rw-r--r-- | gui/src/views/App.css | 5 | ||||
| -rw-r--r-- | gui/src/views/App.tsx | 19 | ||||
| -rw-r--r-- | gui/src/views/budgetUpdatesTable/BudgetUpdatesTableView.css | 10 | ||||
| -rw-r--r-- | gui/src/views/budgetUpdatesTable/BudgetUpdatesTableView.tsx | 32 | ||||
| -rw-r--r-- | gui/src/views/histogram/HistogramView.tsx (renamed from gui/src/components/app/App.tsx) | 14 |
14 files changed, 195 insertions, 17 deletions
diff --git a/gui/src/ajax/backend.ts b/gui/src/ajax/backend.ts index 90a7ea5..4a2462f 100644 --- a/gui/src/ajax/backend.ts +++ b/gui/src/ajax/backend.ts @@ -1,5 +1,6 @@ import { BalanceMap, newCategoryBalanceMapFromDto } from "../types/balanceMap"; import { BudgetMap, newBudgetMapFromDto } from "../types/budgetMap"; +import { BudgetUpdate, newBudgetUpdateFromDto } from "../types/budgetUpdate"; import { Category, newCategoryFromDto } from "../types/category"; const backendUrl = "http://localhost:8000"; @@ -23,6 +24,13 @@ function makeGetter<T>(params: FetcherMakerParams<T>): Getter<T> { }; } +export const getAllBudgetUpdates: Getter<BudgetUpdate[]> = makeGetter< + BudgetUpdate[] +>({ + path: "/budget-update", + elementParser: newBudgetUpdateFromDto, +}); + export const getAllCategories: Getter<Category[]> = async () => { const balances = await getAllCategoryBalances(); const budgets = await getAllCurrentCategoryBudgets(); diff --git a/gui/src/components/app/App.css b/gui/src/components/BudgetUpdatesTable/BudgetUpdatesTable.css index 8b13789..8b13789 100644 --- a/gui/src/components/app/App.css +++ b/gui/src/components/BudgetUpdatesTable/BudgetUpdatesTable.css diff --git a/gui/src/components/BudgetUpdatesTable/BudgetUpdatesTable.tsx b/gui/src/components/BudgetUpdatesTable/BudgetUpdatesTable.tsx new file mode 100644 index 0000000..479d69f --- /dev/null +++ b/gui/src/components/BudgetUpdatesTable/BudgetUpdatesTable.tsx @@ -0,0 +1,36 @@ +import { BudgetUpdate } from '../../types/budgetUpdate'; +import './BudgetUpdatesTable.css'; + +function BudgetUpdatesTable({ budgetUpdates }: Props) { + return ( + <table> + <thead> + <tr> + <td>Date</td> + <td>Quantity</td> + <td>Period</td> + </tr> + </thead> + + <tbody> + { budgetUpdates.map(Row) } + </tbody> + </table> + ); +} + +function Row(budgetUpdate: BudgetUpdate) { + return ( + <tr> + <td>{budgetUpdate.date.format("DD Mm YYYY")}</td> + <td>{budgetUpdate.newQuantity.toString()}</td> + <td>{budgetUpdate.newPeriod}</td> + </tr> + ) +} + +type Props = { + budgetUpdates: BudgetUpdate[]; +} + +export { BudgetUpdatesTable }; diff --git a/gui/src/components/histogram/Histogram.tsx b/gui/src/components/histogram/Histogram.tsx index 3a8309b..1dd8c65 100644 --- a/gui/src/components/histogram/Histogram.tsx +++ b/gui/src/components/histogram/Histogram.tsx @@ -1,5 +1,7 @@ import { BalanceToBudgetRatio } from "../../types/balanceToBudgetRatio"; import { Category } from "../../types/category"; +import { Group } from "../../types/group"; +import { groupCategories } from "../../util/groupCategories"; import './Histogram.scss'; function Histogram({ categories }: Props) { @@ -14,6 +16,8 @@ function Histogram({ categories }: Props) { category.balanceToBudgetRatio = balanceToBudgetRatioNormaliser(category.balanceToBudgetRatio); } + const groups = [...groupCategories(categories)]; + return ( <table> <thead> @@ -23,18 +27,26 @@ function Histogram({ categories }: Props) { <th>Balance</th> </tr> </thead> - - <tbody> - { - categories - .sort((c1, c2) => BalanceToBudgetRatio.compare(c1.balanceToBudgetRatio, c2.balanceToBudgetRatio)) - .map((category) => Row({ category })) - } - </tbody> + { + groups.map(g => <RowGroup group={g} />) + } </table> ); } +function RowGroup({ group }: GroupProps) { + return ( + <tbody> + <tr role="heading" aria-level="3">{group.name}</tr> + { + group.categories + .sort((c1, c2) => BalanceToBudgetRatio.compare(c1.balanceToBudgetRatio, c2.balanceToBudgetRatio)) + .map((category) => Row({ category })) + } + </tbody> + ); +} + function Row({ category }: RowProps) { return ( <tr> @@ -57,6 +69,10 @@ type Props = { categories: Category[] | null, }; +type GroupProps = { + group: Group; +} + type RowProps = { category: Category, }; diff --git a/gui/src/index.tsx b/gui/src/index.tsx index eec284c..d2f18a0 100644 --- a/gui/src/index.tsx +++ b/gui/src/index.tsx @@ -2,7 +2,7 @@ import { render } from 'solid-js/web' import './index.css' -import App from './components/app/App' +import App from './views/App' const root = document.getElementById('root') diff --git a/gui/src/types/budgetUpdate.ts b/gui/src/types/budgetUpdate.ts new file mode 100644 index 0000000..0696f8a --- /dev/null +++ b/gui/src/types/budgetUpdate.ts @@ -0,0 +1,31 @@ +import moment, { Moment } from "moment"; +import Currency from "./currency/currency"; + +interface BudgetUpdate { + id: number; + categoryId: number; + date: Moment; + newQuantity: Currency; + newPeriod: number; +} + +interface BudgetUpdateDto { + id: number; + category_id: number; + date: string; + new_budget: number; + new_period: number; +} + +function newBudgetUpdateFromDto(dto: unknown): BudgetUpdate { + const typedDto = dto as BudgetUpdateDto; + return { + id: typedDto.id, + categoryId: typedDto.category_id, + date: moment(typedDto.date), + newQuantity: typedDto.new_budget, + newPeriod: typedDto.new_period, + }; +} + +export { type BudgetUpdate, type BudgetUpdateDto, newBudgetUpdateFromDto }; diff --git a/gui/src/types/category.ts b/gui/src/types/category.ts index 005c041..9c2a651 100644 --- a/gui/src/types/category.ts +++ b/gui/src/types/category.ts @@ -7,6 +7,7 @@ import Currency from "./currency/currency"; interface Category { id: number; name: string; + groupName: string; currentBalance: Currency; currentBudget: Budget | null; balanceToBudgetRatio: BalanceToBudgetRatio | null; @@ -15,6 +16,7 @@ interface Category { interface CategoryDto { id: number; name: string; + group: string; } function newCategoryFromDto( @@ -32,6 +34,7 @@ function newCategoryFromDto( return { id: dtoDangerous.id, name: dtoDangerous.name, + groupName: dtoDangerous.group, currentBalance, currentBudget, balanceToBudgetRatio: BalanceToBudgetRatio.new( diff --git a/gui/src/types/group.ts b/gui/src/types/group.ts new file mode 100644 index 0000000..4a04019 --- /dev/null +++ b/gui/src/types/group.ts @@ -0,0 +1,8 @@ +import { Category } from "./category"; + +interface Group { + name: string; + categories: Category[]; +} + +export { type Group }; diff --git a/gui/src/util/groupCategories.ts b/gui/src/util/groupCategories.ts new file mode 100644 index 0000000..a0336ea --- /dev/null +++ b/gui/src/util/groupCategories.ts @@ -0,0 +1,12 @@ +import { Category } from "../types/category"; +import { Group } from "../types/group"; + +export function* groupCategories(categories: Category[]): Generator<Group> { + const groupNames = new Set(categories.map(c => c.groupName)); + for (const name of groupNames) { + yield { + name, + categories: categories.filter(c => c.groupName === name) + }; + } +} diff --git a/gui/src/views/App.css b/gui/src/views/App.css new file mode 100644 index 0000000..9775b76 --- /dev/null +++ b/gui/src/views/App.css @@ -0,0 +1,5 @@ +div { + display: flex; + justify-content: space-evenly; + width: 100%; +} diff --git a/gui/src/views/App.tsx b/gui/src/views/App.tsx new file mode 100644 index 0000000..80eb051 --- /dev/null +++ b/gui/src/views/App.tsx @@ -0,0 +1,19 @@ +import { createResource } from 'solid-js'; +import './App.css' +import { getAllBudgetUpdates, getAllCategories } from '../ajax/backend'; +import { HistogramView } from './histogram/HistogramView'; +import { BudgetUpdatesTableView } from './budgetUpdatesTable/BudgetUpdatesTableView'; + +function App() { + const [categories] = createResource(getAllCategories); + const [budgetUpdates] = createResource(getAllBudgetUpdates); + + return ( + <div> + <HistogramView categories={categories} /> + <BudgetUpdatesTableView budgetUpdates={budgetUpdates} categories={categories} /> + </div> + ); +} + +export default App diff --git a/gui/src/views/budgetUpdatesTable/BudgetUpdatesTableView.css b/gui/src/views/budgetUpdatesTable/BudgetUpdatesTableView.css new file mode 100644 index 0000000..e862976 --- /dev/null +++ b/gui/src/views/budgetUpdatesTable/BudgetUpdatesTableView.css @@ -0,0 +1,10 @@ +div { + display: flex; + flex-direction: column; + gap: 1rem; +} + +form { + display: flex; + gap: 1rem; +} diff --git a/gui/src/views/budgetUpdatesTable/BudgetUpdatesTableView.tsx b/gui/src/views/budgetUpdatesTable/BudgetUpdatesTableView.tsx new file mode 100644 index 0000000..465ea92 --- /dev/null +++ b/gui/src/views/budgetUpdatesTable/BudgetUpdatesTableView.tsx @@ -0,0 +1,32 @@ +import { createSignal, Match, Resource, Switch } from 'solid-js'; +import { BudgetUpdate } from '../../types/budgetUpdate'; +import { BudgetUpdatesTable } from '../../components/BudgetUpdatesTable/BudgetUpdatesTable'; +import { Category } from '../../types/category'; +import './BudgetUpdatesTableView.css'; + +export function BudgetUpdatesTableView({ budgetUpdates, categories }: Props) { + const [newCategoryId, setCategoryId] = createSignal<number>(); + + const filteredAndSortedBudgetUpdates: BudgetUpdate[] = budgetUpdates() + ?.filter((bu) => bu.categoryId === newCategoryId()) + ?.sort((bu1, bu2) => bu2.date.diff(bu1.date)) + ?? []; + + return ( + <div> + <form action="none"> + <label for="category">Category</label> + <select id="category" onChange={(e) => setCategoryId(Number(e.target.value))}> + {(categories() ?? []).map((c) => + <option value={c.id}>{c.name}</option>)} + </select> + </form> + <BudgetUpdatesTable budgetUpdates={filteredAndSortedBudgetUpdates as BudgetUpdate[]} /> + </div> + ); +} + +type Props = { + budgetUpdates: Resource<BudgetUpdate[] | null>; + categories: Resource<Category[] | null>; +} diff --git a/gui/src/components/app/App.tsx b/gui/src/views/histogram/HistogramView.tsx index e9ef742..30e64ca 100644 --- a/gui/src/components/app/App.tsx +++ b/gui/src/views/histogram/HistogramView.tsx @@ -1,12 +1,8 @@ -import { createResource, Match, Resource, Switch } from 'solid-js'; -import Histogram from '../histogram/Histogram' -import './App.css' -import { getAllCategories } from '../../ajax/backend'; +import { Match, Resource, Switch } from 'solid-js'; +import Histogram from '../../components/histogram/Histogram' import { Category } from '../../types/category'; -function App() { - const [categories] = createResource(getAllCategories); - +export function HistogramView({ categories }: Props) { return ( <Switch> <Match when={categories.state === 'ready'}> @@ -30,4 +26,6 @@ function App() { ); } -export default App +type Props = { + categories: Resource<Category[] | null>; +} |
