Add a widget to select a movement
This commit is contained in:
127
moneymgr_web/src/widgets/SelectMovementWidget.tsx
Normal file
127
moneymgr_web/src/widgets/SelectMovementWidget.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import { ListItem, ListItemButton, Paper, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { AccountInput } from "./forms/AccountInput";
|
||||
import { AmountInput } from "./forms/AmountInput";
|
||||
import { DateInput } from "./forms/DateInput";
|
||||
import { TextInput } from "./forms/TextInput";
|
||||
import { Movement, MovementApi } from "../api/MovementsApi";
|
||||
import { AsyncWidget } from "./AsyncWidget";
|
||||
import { AsyncMovementWidget, MovementWidget } from "./MovementWidget";
|
||||
|
||||
export function SelectMovementWidget(p: {
|
||||
value?: number;
|
||||
onChange: (movementId: number) => void;
|
||||
initialValues?: {
|
||||
amount?: number;
|
||||
accountId?: number;
|
||||
date?: number;
|
||||
label?: string;
|
||||
};
|
||||
}): React.ReactElement {
|
||||
const [amount, setAmount] = React.useState<number | undefined>(
|
||||
p.initialValues?.amount
|
||||
);
|
||||
const [accountId, setAccountId] = React.useState<number | undefined>(
|
||||
p.initialValues?.accountId
|
||||
);
|
||||
const [date, setDate] = React.useState<number | undefined>(
|
||||
p.initialValues?.date
|
||||
);
|
||||
const [label, setLabel] = React.useState<string | undefined>(
|
||||
p.initialValues?.label
|
||||
);
|
||||
|
||||
const filters = {
|
||||
label: label,
|
||||
amount_min: amount ? amount - 0.5 : undefined,
|
||||
amount_max: amount ? amount + 0.5 : undefined,
|
||||
time_min: date ? date - 3600 * 24 : undefined,
|
||||
time_max: date ? date + 3600 * 24 : undefined,
|
||||
limit: 10,
|
||||
};
|
||||
|
||||
const [list, setList] = React.useState<Movement[] | undefined>();
|
||||
|
||||
const load = async () => {
|
||||
if (accountId)
|
||||
setList(await MovementApi.GetAccountMovements(accountId, filters));
|
||||
else setList(undefined);
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper style={{ padding: "10px" }}>
|
||||
<div
|
||||
style={{ display: "flex", flexDirection: "row", alignItems: "center" }}
|
||||
>
|
||||
<AccountInput
|
||||
value={accountId}
|
||||
onChange={setAccountId}
|
||||
style={{ flex: 20 }}
|
||||
/>
|
||||
<span style={{ flex: 1 }} />
|
||||
<AmountInput
|
||||
editable
|
||||
value={amount ?? 0}
|
||||
onValueChange={setAmount}
|
||||
label="Amount"
|
||||
placeholder="Amount"
|
||||
variant="outlined"
|
||||
style={{ height: "100%", flex: 20 }}
|
||||
/>
|
||||
<span style={{ flex: 1 }} />
|
||||
<DateInput
|
||||
editable
|
||||
value={date}
|
||||
onValueChange={setDate}
|
||||
label="Date"
|
||||
style={{ flex: 20 }}
|
||||
variant="outlined"
|
||||
/>
|
||||
<span style={{ flex: 1 }} />
|
||||
<TextInput
|
||||
editable
|
||||
value={label}
|
||||
onValueChange={setLabel}
|
||||
label="Label"
|
||||
variant="outlined"
|
||||
style={{ flex: 20 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<AsyncWidget
|
||||
loadKey={accountId + "/" + JSON.stringify(filters)}
|
||||
load={load}
|
||||
errMsg="Failed to load the list of movements!"
|
||||
build={() => {
|
||||
if (list === null)
|
||||
return (
|
||||
<Typography style={{ textAlign: "center", padding: "20px" }}>
|
||||
Select an account to begin research.
|
||||
</Typography>
|
||||
);
|
||||
if (list?.length === 0)
|
||||
return (
|
||||
<Typography style={{ textAlign: "center", padding: "20px" }}>
|
||||
No result.
|
||||
</Typography>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{list?.map((entry) => (
|
||||
<ListItem>
|
||||
<ListItemButton
|
||||
selected={entry.id === p.value}
|
||||
onClick={() => p.onChange(entry.id)}
|
||||
>
|
||||
<MovementWidget movement={entry} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Paper>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user