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 { RouterLink } from "../widgets/RouterLink";
|
||||
|
||||
export function BackupRoute(): React.ReactElement {
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
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