Can perform import / export - to / from FinancesManager from UI
This commit is contained in:
parent
dadf959db2
commit
caf61fc21a
23
moneymgr_web/src/api/BackupApi.ts
Normal file
23
moneymgr_web/src/api/BackupApi.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { APIClient } from "./ApiClient";
|
||||||
|
|
||||||
|
export class BackupApi {
|
||||||
|
/**
|
||||||
|
* FinancesManager export
|
||||||
|
*/
|
||||||
|
static get FinancesManagerExportURL(): string {
|
||||||
|
return APIClient.backendURL() + "/backup/finances_manager/export";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FinancesManager import
|
||||||
|
*/
|
||||||
|
static async FinancesManagerImport(file: File): Promise<void> {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append("file", file);
|
||||||
|
await APIClient.exec({
|
||||||
|
method: "POST",
|
||||||
|
uri: "/backup/finances_manager/import",
|
||||||
|
formData: fd,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,148 @@
|
|||||||
|
import { mdiCash } from "@mdi/js";
|
||||||
|
import Icon from "@mdi/react";
|
||||||
|
import DownloadIcon from "@mui/icons-material/Download";
|
||||||
|
import UploadIcon from "@mui/icons-material/Upload";
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
Button,
|
||||||
|
Card,
|
||||||
|
CardActions,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
Grid,
|
||||||
|
Typography,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { BackupApi } from "../api/BackupApi";
|
||||||
|
import { useAccounts } from "../hooks/AccountsListProvider";
|
||||||
|
import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
|
||||||
|
import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider";
|
||||||
|
import { useLoadingMessage } from "../hooks/context_providers/LoadingMessageProvider";
|
||||||
|
import { useSnackbar } from "../hooks/context_providers/SnackbarProvider";
|
||||||
import { MoneyMgrWebRouteContainer } from "../widgets/MoneyMgrWebRouteContainer";
|
import { MoneyMgrWebRouteContainer } from "../widgets/MoneyMgrWebRouteContainer";
|
||||||
|
import { RouterLink } from "../widgets/RouterLink";
|
||||||
|
|
||||||
export function BackupRoute(): React.ReactElement {
|
export function BackupRoute(): React.ReactElement {
|
||||||
return (
|
return (
|
||||||
<MoneyMgrWebRouteContainer label={"Backup & Restore"}>
|
<MoneyMgrWebRouteContainer label={"Backup & Restore"}>
|
||||||
TODO
|
<Grid container>
|
||||||
|
{/* FinancesManager */}
|
||||||
|
<ImportExportModal
|
||||||
|
icon={<Icon path={mdiCash} size={1} />}
|
||||||
|
label="FinancesManager"
|
||||||
|
description={
|
||||||
|
<>
|
||||||
|
Import and export movements using{" "}
|
||||||
|
<a
|
||||||
|
href="https://gitlab.com/pierre42100/cpp-financesmanager"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
style={{ color: "inherit" }}
|
||||||
|
>
|
||||||
|
FinanceManager
|
||||||
|
</a>{" "}
|
||||||
|
file format (does not support file attachments).
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
importWarning="Existing data will not be touched, movement will be inserted in newly created accounts."
|
||||||
|
exportURL={BackupApi.FinancesManagerExportURL}
|
||||||
|
onImport={BackupApi.FinancesManagerImport}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
</MoneyMgrWebRouteContainer>
|
</MoneyMgrWebRouteContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ImportExportModal(p: {
|
||||||
|
icon: React.ReactElement;
|
||||||
|
label: string;
|
||||||
|
description: string | React.ReactElement;
|
||||||
|
importWarning?: string;
|
||||||
|
exportURL: string;
|
||||||
|
onImport?: (file: File) => Promise<void>;
|
||||||
|
}): React.ReactElement {
|
||||||
|
const confirm = useConfirm();
|
||||||
|
const alert = useAlert();
|
||||||
|
const snackbar = useSnackbar();
|
||||||
|
const loadingMessage = useLoadingMessage();
|
||||||
|
const accounts = useAccounts();
|
||||||
|
|
||||||
|
const doImport = async () => {
|
||||||
|
try {
|
||||||
|
const fileEl = document.createElement("input");
|
||||||
|
fileEl.type = "file";
|
||||||
|
fileEl.click();
|
||||||
|
|
||||||
|
// Wait for a file to be chosen
|
||||||
|
await new Promise((res, _rej) =>
|
||||||
|
fileEl.addEventListener("change", () => res(null))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fileEl.files?.length === 0) return;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(await confirm(
|
||||||
|
<>
|
||||||
|
<p>Do you really want to perform import from {p.label}?</p>
|
||||||
|
{p.importWarning && (
|
||||||
|
<Alert severity="warning">{p.importWarning}</Alert>
|
||||||
|
)}
|
||||||
|
</>,
|
||||||
|
`Import from ${p.label}`,
|
||||||
|
"Perform import"
|
||||||
|
))
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
loadingMessage.show("Performing import...");
|
||||||
|
|
||||||
|
await p.onImport!(fileEl.files![0]);
|
||||||
|
|
||||||
|
snackbar("The import was successuflly executed!");
|
||||||
|
accounts.reload();
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to perform import!", e);
|
||||||
|
alert(`Failed to perform import! ${e}`);
|
||||||
|
} finally {
|
||||||
|
loadingMessage.hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid size={{ md: 6 }}>
|
||||||
|
<Card sx={{ maxWidth: 345 }} elevation={3} variant="outlined">
|
||||||
|
<CardHeader avatar={p.icon} title={p.label} />
|
||||||
|
<CardContent>
|
||||||
|
<Typography style={{ textAlign: "justify" }}>
|
||||||
|
{p.description}
|
||||||
|
</Typography>
|
||||||
|
</CardContent>{" "}
|
||||||
|
<CardActions>
|
||||||
|
<span style={{ flex: 1 }}>
|
||||||
|
<RouterLink to={p.exportURL}>
|
||||||
|
<Button
|
||||||
|
startIcon={<DownloadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="info"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
</RouterLink>
|
||||||
|
</span>
|
||||||
|
<span style={{ flex: 1 }}>
|
||||||
|
<Button
|
||||||
|
startIcon={<UploadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="warning"
|
||||||
|
fullWidth
|
||||||
|
disabled={!p.onImport}
|
||||||
|
onClick={doImport}
|
||||||
|
>
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user