Created a dialog to select an account
This commit is contained in:
		
							
								
								
									
										82
									
								
								moneymgr_web/src/dialogs/SelectAccountDialog.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								moneymgr_web/src/dialogs/SelectAccountDialog.tsx
									
									
									
									
									
										Normal 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>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -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)!;
 | 
			
		||||
}
 | 
			
		||||
@@ -16,6 +16,7 @@ import { DateWidget } from "../widgets/DateWidget";
 | 
			
		||||
import { MoneyMgrWebRouteContainer } from "../widgets/MoneyMgrWebRouteContainer";
 | 
			
		||||
import { NewMovementWidget } from "../widgets/NewMovementWidget";
 | 
			
		||||
import { NotFoundRoute } from "./NotFound";
 | 
			
		||||
import { useSelectAccount } from "../hooks/context_providers/ChooseAccountDialogProvider";
 | 
			
		||||
 | 
			
		||||
export function AccountRoute(): React.ReactElement {
 | 
			
		||||
  const loadingMessage = useLoadingMessage();
 | 
			
		||||
@@ -87,12 +88,23 @@ function MovementsTable(p: {
 | 
			
		||||
  movements: Movement[];
 | 
			
		||||
  needReload: (skipMovements: boolean) => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const accounts = useAccounts();
 | 
			
		||||
  const alert = useAlert();
 | 
			
		||||
  const confirm = useConfirm();
 | 
			
		||||
 | 
			
		||||
  const chooseAccount = useSelectAccount();
 | 
			
		||||
 | 
			
		||||
  // Change account of 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
 | 
			
		||||
    alert(target?.id?.toString() ?? "none");
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  // Delete movement
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ import { AccountsListProvider } from "../hooks/AccountsListProvider";
 | 
			
		||||
import { AsyncWidget } from "./AsyncWidget";
 | 
			
		||||
import { MoneyNavList } from "./MoneyNavList";
 | 
			
		||||
import { MoneyWebAppBar } from "./MoneyWebAppBar";
 | 
			
		||||
import { ChooseAccountDialogProvider } from "../hooks/context_providers/ChooseAccountDialogProvider";
 | 
			
		||||
 | 
			
		||||
interface AuthInfoContext {
 | 
			
		||||
  info: AuthInfo;
 | 
			
		||||
@@ -48,44 +49,46 @@ export function BaseAuthenticatedPage(): React.ReactElement {
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <AccountsListProvider>
 | 
			
		||||
            <Box
 | 
			
		||||
              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} />
 | 
			
		||||
 | 
			
		||||
            <ChooseAccountDialogProvider>
 | 
			
		||||
              <Box
 | 
			
		||||
                component="div"
 | 
			
		||||
                sx={{
 | 
			
		||||
                  minHeight: "100vh",
 | 
			
		||||
                  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 />
 | 
			
		||||
                <div
 | 
			
		||||
                  style={{
 | 
			
		||||
                    flexGrow: 1,
 | 
			
		||||
                    flexShrink: 0,
 | 
			
		||||
                    flexBasis: 0,
 | 
			
		||||
                    minWidth: 0,
 | 
			
		||||
                <MoneyWebAppBar onSignOut={signOut} />
 | 
			
		||||
 | 
			
		||||
                <Box
 | 
			
		||||
                  sx={{
 | 
			
		||||
                    display: "flex",
 | 
			
		||||
                    flex: "2",
 | 
			
		||||
                  }}
 | 
			
		||||
                >
 | 
			
		||||
                  <Outlet />
 | 
			
		||||
                </div>
 | 
			
		||||
                  <MoneyNavList />
 | 
			
		||||
                  <div
 | 
			
		||||
                    style={{
 | 
			
		||||
                      flexGrow: 1,
 | 
			
		||||
                      flexShrink: 0,
 | 
			
		||||
                      flexBasis: 0,
 | 
			
		||||
                      minWidth: 0,
 | 
			
		||||
                      display: "flex",
 | 
			
		||||
                    }}
 | 
			
		||||
                  >
 | 
			
		||||
                    <Outlet />
 | 
			
		||||
                  </div>
 | 
			
		||||
                </Box>
 | 
			
		||||
              </Box>
 | 
			
		||||
            </Box>
 | 
			
		||||
            </ChooseAccountDialogProvider>
 | 
			
		||||
          </AccountsListProvider>
 | 
			
		||||
        </AuthInfoContextK>
 | 
			
		||||
      )}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user