From 14501ec72dfa279e64d3a1768e09b6e05249581b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Loipf=C3=BChrer?= Date: Sun, 11 Feb 2024 10:11:25 +0100 Subject: [PATCH] feat(web): add purchase for account from account detail page --- CHANGELOG.md | 2 + .../accounts/AccountTransactionList.tsx | 112 +++++++++++++----- .../accounts/ClearingAccountDetail.tsx | 47 ++++---- .../accounts/AccountDetail/AccountDetail.tsx | 4 +- frontend/libs/translations/src/lib/en.ts | 1 + 5 files changed, 109 insertions(+), 57 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0b73b9..184a3fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ [Compare the full difference.](https://github.com/SFTtech/abrechnung/compare/v0.13.2...HEAD) +- web: add button to add a new purchase for an account from the account detail page + ## 0.13.1 (2024-02-11) [Compare the full difference.](https://github.com/SFTtech/abrechnung/compare/v0.13.1...v0.13.2) diff --git a/frontend/apps/web/src/components/accounts/AccountTransactionList.tsx b/frontend/apps/web/src/components/accounts/AccountTransactionList.tsx index 1961d45..e0031b4 100644 --- a/frontend/apps/web/src/components/accounts/AccountTransactionList.tsx +++ b/frontend/apps/web/src/components/accounts/AccountTransactionList.tsx @@ -1,60 +1,108 @@ -import { selectClearingAccountsInvolvingAccounts, selectTransactionsInvolvingAccount } from "@abrechnung/redux"; +import { + createTransaction, + selectClearingAccountsInvolvingAccounts, + selectTransactionsInvolvingAccount, +} from "@abrechnung/redux"; +import { Add as AddIcon } from "@mui/icons-material"; import { Account, Transaction } from "@abrechnung/types"; -import { Alert, List } from "@mui/material"; +import { Alert, Box, IconButton, List, Tooltip, Typography } from "@mui/material"; import { DateTime } from "luxon"; import * as React from "react"; -import { selectAccountSlice, selectTransactionSlice, useAppSelector } from "@/store"; +import { selectAccountSlice, selectTransactionSlice, useAppDispatch, useAppSelector } from "@/store"; import { AccountClearingListEntry } from "./AccountClearingListEntry"; import { AccountTransactionListEntry } from "./AccountTransactionListEntry"; +import { useTranslation } from "react-i18next"; +import { toast } from "react-toastify"; +import { useNavigate } from "react-router-dom"; +import { PurchaseIcon } from "../style/AbrechnungIcons"; type ArrayAccountsAndTransactions = Array; interface Props { groupId: number; - accountId: number; + account: Account; } -export const AccountTransactionList: React.FC = ({ groupId, accountId }) => { +export const AccountTransactionList: React.FC = ({ groupId, account }) => { + const { t } = useTranslation(); + const dispatch = useAppDispatch(); + const navigate = useNavigate(); const transactions = useAppSelector((state) => - selectTransactionsInvolvingAccount({ state: selectTransactionSlice(state), groupId, accountId }) + selectTransactionsInvolvingAccount({ state: selectTransactionSlice(state), groupId, accountId: account.id }) ); const clearingAccounts = useAppSelector((state) => - selectClearingAccountsInvolvingAccounts({ state: selectAccountSlice(state), groupId, accountId }) + selectClearingAccountsInvolvingAccounts({ state: selectAccountSlice(state), groupId, accountId: account.id }) ); const combinedList: ArrayAccountsAndTransactions = (transactions as ArrayAccountsAndTransactions) .concat(clearingAccounts) .sort((f1, f2) => DateTime.fromISO(f2.last_changed).toMillis() - DateTime.fromISO(f1.last_changed).toMillis()); + const createTransactionForAccount = () => { + dispatch( + createTransaction({ + groupId, + type: "purchase", + data: { + debitor_shares: { + [account.id]: 1, + }, + }, + }) + ) + .unwrap() + .then(({ transaction }) => { + navigate(`/groups/${groupId}/transactions/${transaction.id}?no-redirect=true`); + }) + .catch(() => toast.error("Creating a transaction failed")); + }; + return ( - - {combinedList.length === 0 && None so far.} - {combinedList.map((entry) => { - if (entry.type === "clearing") { + <> + + {t("accounts.transactionsInvolving", "", { account })} + + + + + + + + + + + {combinedList.length === 0 && None so far.} + {combinedList.map((entry) => { + if (entry.type === "clearing") { + return ( + + ); + } + if (entry.type === "personal") { + return null; + } + + // we need to case "entry" to Transaction as typescript cant deduce that it + // has to be a transaction even though we handled all other "type" cases before return ( - ); - } - if (entry.type === "personal") { - return null; - } - - // we need to case "entry" to Transaction as typescript cant deduce that it - // has to be a transaction even though we handled all other "type" cases before - return ( - - ); - })} - + })} + + ); }; diff --git a/frontend/apps/web/src/components/accounts/ClearingAccountDetail.tsx b/frontend/apps/web/src/components/accounts/ClearingAccountDetail.tsx index bab75d8..06c0d83 100644 --- a/frontend/apps/web/src/components/accounts/ClearingAccountDetail.tsx +++ b/frontend/apps/web/src/components/accounts/ClearingAccountDetail.tsx @@ -1,5 +1,5 @@ import { selectAccountBalances, selectAccountById, selectGroupCurrencySymbol } from "@abrechnung/redux"; -import { TableCell } from "@mui/material"; +import { TableCell, Typography } from "@mui/material"; import React from "react"; import { selectAccountSlice, selectGroupSlice, useAppSelector } from "@/store"; import { ShareSelect } from "../ShareSelect"; @@ -26,26 +26,29 @@ export const ClearingAccountDetail: React.FC = ({ groupId, accountId }) = } return ( - - {t("common.shared")} - - } - excludeAccounts={[account.id]} - renderAdditionalShareInfo={({ account: participatingAccount }) => ( - - {formatCurrency( - balances[account.id]?.clearingResolution[participatingAccount.id] ?? 0, - currency_symbol - )} - - )} - onChange={(value) => undefined} - editable={false} - /> + <> + {t("accounts.clearingDistributionOf", "", { account })} + + {t("common.shared")} + + } + excludeAccounts={[account.id]} + renderAdditionalShareInfo={({ account: participatingAccount }) => ( + + {formatCurrency( + balances[account.id]?.clearingResolution[participatingAccount.id] ?? 0, + currency_symbol + )} + + )} + onChange={(value) => undefined} + editable={false} + /> + ); }; diff --git a/frontend/apps/web/src/pages/accounts/AccountDetail/AccountDetail.tsx b/frontend/apps/web/src/pages/accounts/AccountDetail/AccountDetail.tsx index d85571f..2b0ddae 100644 --- a/frontend/apps/web/src/pages/accounts/AccountDetail/AccountDetail.tsx +++ b/frontend/apps/web/src/pages/accounts/AccountDetail/AccountDetail.tsx @@ -76,15 +76,13 @@ export const AccountDetail: React.FC = ({ groupId }) => { {account.type === "clearing" && ( - {t("accounts.clearingDistributionOf", "", { account })} )} - {t("accounts.transactionsInvolving", "", { account })} - + diff --git a/frontend/libs/translations/src/lib/en.ts b/frontend/libs/translations/src/lib/en.ts index 3348465..bdd36ba 100644 --- a/frontend/libs/translations/src/lib/en.ts +++ b/frontend/libs/translations/src/lib/en.ts @@ -133,6 +133,7 @@ const translations = { transactions: { createTransaction: "Create Transaction", createPurchase: "Create Purchase", + createPurchaseForAccount: "Create purchase with {{accountName}} as participant", createTransfer: "Create Transfer", noTransactions: "No Transactions", purchase: "Purchase",