Created a dialog to select an account

This commit is contained in:
Pierre HUBERT 2025-04-22 20:23:25 +02:00
parent 2afedcd5f9
commit 639fa4b176
4 changed files with 202 additions and 29 deletions

View File

@ -0,0 +1,82 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
FormControlLabel,
ListItem,
ListItemIcon,
ListItemText,
Radio,
RadioGroup,
} from "@mui/material";
import React from "react";
import { Account } from "../api/AccountApi";
import { useAccounts } from "../hooks/AccountsListProvider";
import { AccountWidget } from "../widgets/AccountWidget";
import { AmountWidget } from "../widgets/AmountWidget";
export function SelectAccountDialog(p: {
open: boolean;
onClose: () => void;
onSelected: (c: Account) => void;
title: string;
description: string;
confirmButton?: string;
excludedAccounts?: Account[];
}): React.ReactElement {
const accounts = useAccounts();
const [choice, setChoice] = React.useState<Account | null>(null);
console.log(choice);
const submit = () => {
if (choice) p.onSelected(choice);
};
return (
<Dialog open={p.open} onClose={p.onClose}>
<DialogTitle>{p.title}</DialogTitle>
<DialogContent dividers>
<DialogContentText>{p.description}</DialogContentText>
<RadioGroup>
{accounts.list.list.map((option) => (
<FormControlLabel
value={option.id}
key={option.id}
control={
<Radio
disabled={
p.excludedAccounts?.find((a) => a.id === option.id) !==
undefined
}
checked={option.id === choice?.id}
onChange={() => setChoice(option)}
/>
}
label={
<ListItem>
<ListItemIcon>
<AccountWidget account={option} />
</ListItemIcon>
<ListItemText
primary={option.name}
secondary={<AmountWidget amount={option.balance} />}
/>
</ListItem>
}
/>
))}
</RadioGroup>
</DialogContent>
<DialogActions>
<Button onClick={p.onClose}>Cancel</Button>
<Button onClick={submit} autoFocus disabled={choice === null}>
{p.confirmButton ?? "Submit"}
</Button>
</DialogActions>
</Dialog>
);
}

View File

@ -0,0 +1,76 @@
import React from "react";
import { Account } from "../../api/AccountApi";
import { SelectAccountDialog } from "../../dialogs/SelectAccountDialog";
type DialogContext = (
title: string,
description: string,
confirmButton?: string,
excludedAccounts?: Account[]
) => Promise<Account | undefined>;
const DialogContextK = React.createContext<DialogContext | null>(null);
export function ChooseAccountDialogProvider(
p: React.PropsWithChildren
): React.ReactElement {
const [title, setTitle] = React.useState("");
const [description, setDescription] = React.useState("");
const [confirmButton, setConfirmButton] = React.useState<
string | undefined
>();
const [excludedAccounts, setExcludedAccounts] = React.useState<
Account[] | undefined
>();
const [open, setOpen] = React.useState(false);
const cb = React.useRef<null | ((a: Account | undefined) => void)>(null);
const handleClose = (res?: Account) => {
setOpen(false);
if (cb.current !== null) cb.current(res);
cb.current = null;
};
const hook: DialogContext = (
title,
description,
confirmButton,
excludedAccounts
) => {
setTitle(title);
setDescription(description);
setConfirmButton(confirmButton);
setExcludedAccounts(excludedAccounts);
setOpen(true);
return new Promise((res) => {
cb.current = res;
});
};
return (
<>
<DialogContextK.Provider value={hook}>
{p.children}
</DialogContextK.Provider>
{open && (
<SelectAccountDialog
open={open}
onClose={handleClose}
onSelected={handleClose}
title={title}
description={description}
confirmButton={confirmButton}
excludedAccounts={excludedAccounts}
/>
)}
</>
);
}
export function useSelectAccount(): DialogContext {
return React.useContext(DialogContextK)!;
}

View File

@ -16,6 +16,7 @@ import { DateWidget } from "../widgets/DateWidget";
import { MoneyMgrWebRouteContainer } from "../widgets/MoneyMgrWebRouteContainer"; import { MoneyMgrWebRouteContainer } from "../widgets/MoneyMgrWebRouteContainer";
import { NewMovementWidget } from "../widgets/NewMovementWidget"; import { NewMovementWidget } from "../widgets/NewMovementWidget";
import { NotFoundRoute } from "./NotFound"; import { NotFoundRoute } from "./NotFound";
import { useSelectAccount } from "../hooks/context_providers/ChooseAccountDialogProvider";
export function AccountRoute(): React.ReactElement { export function AccountRoute(): React.ReactElement {
const loadingMessage = useLoadingMessage(); const loadingMessage = useLoadingMessage();
@ -87,12 +88,23 @@ function MovementsTable(p: {
movements: Movement[]; movements: Movement[];
needReload: (skipMovements: boolean) => void; needReload: (skipMovements: boolean) => void;
}): React.ReactElement { }): React.ReactElement {
const accounts = useAccounts();
const alert = useAlert(); const alert = useAlert();
const confirm = useConfirm(); const confirm = useConfirm();
const chooseAccount = useSelectAccount();
// Change account of movement // Change account of movement
const handleMoveClick = async (movement: Movement) => { const handleMoveClick = async (movement: Movement) => {
const target = await chooseAccount(
"Transfer movement",
`Please select the target account that will receive the movement: ${movement.label} (${movement.amount} €)`,
"Transfer movement",
[accounts.get(movement.account_id)!]
);
// TODO // TODO
alert(target?.id?.toString() ?? "none");
}; };
// Delete movement // Delete movement

View File

@ -7,6 +7,7 @@ import { AccountsListProvider } from "../hooks/AccountsListProvider";
import { AsyncWidget } from "./AsyncWidget"; import { AsyncWidget } from "./AsyncWidget";
import { MoneyNavList } from "./MoneyNavList"; import { MoneyNavList } from "./MoneyNavList";
import { MoneyWebAppBar } from "./MoneyWebAppBar"; import { MoneyWebAppBar } from "./MoneyWebAppBar";
import { ChooseAccountDialogProvider } from "../hooks/context_providers/ChooseAccountDialogProvider";
interface AuthInfoContext { interface AuthInfoContext {
info: AuthInfo; info: AuthInfo;
@ -48,44 +49,46 @@ export function BaseAuthenticatedPage(): React.ReactElement {
}} }}
> >
<AccountsListProvider> <AccountsListProvider>
<Box <ChooseAccountDialogProvider>
component="div"
sx={{
minHeight: "100vh",
display: "flex",
flexDirection: "column",
backgroundColor: (theme) =>
theme.palette.mode === "light"
? theme.palette.grey[100]
: theme.palette.grey[900],
color: (theme) =>
theme.palette.mode === "light"
? theme.palette.grey[900]
: theme.palette.grey[100],
}}
>
<MoneyWebAppBar onSignOut={signOut} />
<Box <Box
component="div"
sx={{ sx={{
minHeight: "100vh",
display: "flex", display: "flex",
flex: "2", flexDirection: "column",
backgroundColor: (theme) =>
theme.palette.mode === "light"
? theme.palette.grey[100]
: theme.palette.grey[900],
color: (theme) =>
theme.palette.mode === "light"
? theme.palette.grey[900]
: theme.palette.grey[100],
}} }}
> >
<MoneyNavList /> <MoneyWebAppBar onSignOut={signOut} />
<div
style={{ <Box
flexGrow: 1, sx={{
flexShrink: 0,
flexBasis: 0,
minWidth: 0,
display: "flex", display: "flex",
flex: "2",
}} }}
> >
<Outlet /> <MoneyNavList />
</div> <div
style={{
flexGrow: 1,
flexShrink: 0,
flexBasis: 0,
minWidth: 0,
display: "flex",
}}
>
<Outlet />
</div>
</Box>
</Box> </Box>
</Box> </ChooseAccountDialogProvider>
</AccountsListProvider> </AccountsListProvider>
</AuthInfoContextK> </AuthInfoContextK>
)} )}