Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): improved position validation, better transfer creation #164

Merged
merged 1 commit into from
Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
/* eslint-disable react/prop-types */
import { TransactionSortMode } from "@abrechnung/core";
import {
createPurchase,
createTransfer,
createTransaction,
fetchTransactions,
selectCurrentUserPermissions,
selectGroupTransactionsStatus,
selectSortedTransactions,
} from "@abrechnung/redux";
import { Transaction, TransactionType } from "@abrechnung/types";
import { toISODateString } from "@abrechnung/utils";
import { useIsFocused } from "@react-navigation/native";
import * as React from "react";
import { useLayoutEffect, useState } from "react";
Expand Down Expand Up @@ -121,50 +119,15 @@ export const TransactionList: React.FC<Props> = ({ navigation }) => {
}, [isFocused, showSearchInput, isMenuOpen, search, sortMode, theme, navigation]);

const createNewTransaction = (type: TransactionType) => {
if (type === "purchase") {
dispatch(createPurchase({ groupId }))
.unwrap()
.then(({ transaction }) => {
navigation.navigate("TransactionDetail", {
transactionId: transaction.id,
groupId: transaction.groupID,
editing: true,
});
});
} else if (type === "transfer") {
dispatch(
createTransfer({
transaction: {
type: "transfer",
groupID: groupId,
name: "",
description: "",
tags: [],
billedAt: toISODateString(new Date()),
currencyConversionRate: 1.0,
currencySymbol: "€",
debitorShares: {},
creditorShares: {},
value: 0,
},
keepWip: true,
api,
})
)
.unwrap()
.then(({ transaction }) => {
navigation.navigate("TransactionDetail", {
transactionId: transaction.id,
groupId: transaction.groupID,
editing: true,
});
})
.catch(() => {
console.log("error creating new transaction");
dispatch(createTransaction({ groupId, type }))
.unwrap()
.then(({ transaction }) => {
navigation.navigate("TransactionDetail", {
transactionId: transaction.id,
groupId: transaction.groupID,
editing: true,
});
} else {
console.error("unknown transaction type");
}
});
};

if (transactionStatus === "loading") {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.sidebarContainer {
min-height: 100%;
margin-bottom: -25px;
}

.sidebarContainer:after {
content: "";
display: block;
}

.footer,
.sidebarContainer:after {
height: 25px;
}

.footer {
display: flex;
padding: 1em;
justify-content: center;
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import { config } from "../../state/config";
import { useTheme } from "@mui/material/styles";
import { Banner } from "../../components/style/Banner";
import Loading from "../../components/style/Loading";
import styles from "./AuthenticatedLayout.module.css";

const drawerWidth = 240;
const AUTH_FALLBACK = "/login";
Expand Down Expand Up @@ -77,9 +78,8 @@ export const AuthenticatedLayout: React.FC = () => {
};

const drawer = (
<div style={{ height: "100%" }}>
<div className={styles["sidebarContainer"]}>
<Toolbar />
<Divider />
{groupId !== undefined && (
<List sx={{ pb: 0 }}>
<ListItemLink
Expand Down Expand Up @@ -163,16 +163,10 @@ export const AuthenticatedLayout: React.FC = () => {
)}
<SidebarGroupList activeGroupId={groupId} />

<Box
sx={{
display: "flex",
position: "absolute",
width: "100%",
justifyContent: "center",
bottom: 0,
padding: 1,
borderTop: 1,
borderColor: theme.palette.divider,
<div
className={styles["footer"]}
style={{
borderTop: `1px solid ${theme.palette.divider}`,
}}
>
{cfg.imprintURL && (
Expand All @@ -192,7 +186,7 @@ export const AuthenticatedLayout: React.FC = () => {
</Link>
</Tooltip>
)}
</Box>
</div>
</div>
);

Expand All @@ -202,8 +196,6 @@ export const AuthenticatedLayout: React.FC = () => {
<AppBar
position="fixed"
sx={{
// width: {sm: `calc(100% - ${drawerWidth}px)`},
// ml: {sm: `${drawerWidth}px`},
zIndex: (theme) => theme.zIndex.drawer + 1,
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@ export const SidebarGroupList: React.FC<Props> = ({ activeGroupId }) => {
<ListItem sx={{ pt: 0, pb: 0 }}>
<ListItemText secondary="Groups" />
</ListItem>
{groups.map((it) => (
<ListItemLink
key={it.id}
to={`/groups/${it.id}`}
selected={activeGroupId && activeGroupId === it.id}
>
<ListItemText primary={it.name} />
</ListItemLink>
))}
<div>
{groups.map((it) => (
<ListItemLink
key={it.id}
to={`/groups/${it.id}`}
selected={activeGroupId && activeGroupId === it.id}
>
<ListItemText primary={it.name} />
</ListItemLink>
))}
</div>
{!isGuest && (
<ListItem sx={{ padding: 0 }}>
<Grid container justifyContent="center">
Expand Down
6 changes: 6 additions & 0 deletions frontend/apps/web/src/components/ShareSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ export const ShareSelect: React.FC<ShareSelectProps> = ({

const [showAdvanced, setShowAdvanced] = React.useState(false);

React.useEffect(() => {
if (Object.values(value).reduce((showAdvanced, value) => showAdvanced || value !== 1, false)) {
setShowAdvanced(true);
}
}, [value, setShowAdvanced]);

const nSelectedPeople = accounts.reduce((nAccs: number, acc: Account) => {
if (acc.type !== "personal") {
return nAccs;
Expand Down
124 changes: 26 additions & 98 deletions frontend/apps/web/src/components/accounts/ClearingAccountDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,118 +1,46 @@
import { selectAccountBalances, selectAccountById, selectGroupCurrencySymbol } from "@abrechnung/redux";
import { TableCell } from "@mui/material";
import React from "react";
import { Grid, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material";
import { Link } from "react-router-dom";
import { useEffect, useState } from "react";
import { ClearingAccountIcon, PersonalAccountIcon } from "../style/AbrechnungIcons";
import { useTheme } from "@mui/material/styles";
import { useAppSelector, selectAccountSlice, selectGroupSlice } from "../../store";
import {
selectAccountBalances,
selectAccountById,
selectGroupAccounts,
selectGroupCurrencySymbol,
} from "@abrechnung/redux";
import { getAccountLink } from "../../utils";
import { selectAccountSlice, selectGroupSlice, useAppSelector } from "../../store";
import { ShareSelect } from "../ShareSelect";

interface Props {
groupId: number;
accountId: number;
}

export const ClearingAccountDetail: React.FC<Props> = ({ groupId, accountId }) => {
const theme = useTheme();

const account = useAppSelector((state) =>
selectAccountById({ state: selectAccountSlice(state), groupId, accountId })
);
const referencedAccounts = useAppSelector((state) =>
selectGroupAccounts({ state: selectAccountSlice(state), groupId })
);
const currencySymbol = useAppSelector((state) =>
selectGroupCurrencySymbol({ state: selectGroupSlice(state), groupId })
);
const balances = useAppSelector((state) => selectAccountBalances({ state, groupId }));

const [showAdvanced, setShowAdvanced] = useState(false);

useEffect(() => {
if (account.type !== "clearing") {
return;
}
for (const share of Object.values(account.clearingShares)) {
if (share !== 1) {
setShowAdvanced(true);
break;
}
}
}, [account]);

const clearingShareValue = (accountId) => {
if (account.type !== "clearing") {
return 0;
}
return account.clearingShares[accountId] ?? 0;
};
if (account.type !== "clearing") {
throw new Error("expected a clearing account to render ClearingAccountDetail, but got a personal account");
}

return (
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell>Account</TableCell>
{showAdvanced && <TableCell>Shares</TableCell>}
<TableCell width="100px" align="right">
Shared
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{referencedAccounts
.filter((a) => balances[account.id]?.clearingResolution[a.id] !== undefined)
.map((a) => (
<TableRow hover key={a.id}>
<TableCell sx={{ padding: "0 16px" }}>
{/*TODO: proper link*/}
<Link
style={{
color: theme.palette.text.primary,
textDecoration: "none",
display: "block",
height: "100%",
width: "100%",
padding: "16px 0",
}}
to={getAccountLink(groupId, a.type, a.id)}
>
<Grid container direction="row" alignItems="center">
<Grid item>
{a.type === "personal" ? (
<PersonalAccountIcon />
) : (
<ClearingAccountIcon />
)}
</Grid>
<Grid item sx={{ ml: 1, display: "flex", flexDirection: "column" }}>
<Typography variant="body2" component="span">
{a.name}
</Typography>
{a.type === "clearing" && a.dateInfo != null && (
<Typography variant="caption" component="span">
{a.dateInfo}
</Typography>
)}
</Grid>
</Grid>
</Link>
</TableCell>
{showAdvanced && <TableCell width="50px">{clearingShareValue(a.id)}</TableCell>}
<TableCell width="100px" align="right">
{balances[account.id]?.clearingResolution[a.id]?.toFixed(2)} {currencySymbol}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<ShareSelect
groupId={groupId}
label="Participated"
value={account.clearingShares}
additionalShareInfoHeader={
<TableCell width="100px" align="right">
Shared
</TableCell>
}
excludeAccounts={[account.id]}
renderAdditionalShareInfo={({ account: participatingAccount }) => (
<TableCell width="100px" align="right">
{(balances[account.id]?.clearingResolution[participatingAccount.id] ?? 0).toFixed(2)}{" "}
{currencySymbol}
</TableCell>
)}
onChange={(value) => undefined}
editable={false}
/>
);
};

Expand Down
Loading