All checks were successful
continuous-integration/drone/push Build is passing
189 lines
5.5 KiB
TypeScript
189 lines
5.5 KiB
TypeScript
import { mdiCash, mdiFolderZipOutline } 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";
|
|
import excelIcon from "./excel.svg";
|
|
|
|
export function BackupRoute(): React.ReactElement {
|
|
return (
|
|
<MoneyMgrWebRouteContainer label={"Backup & Restore"}>
|
|
<Grid container spacing={2}>
|
|
{/* ZIP */}
|
|
<ImportExportModal
|
|
icon={<Icon path={mdiFolderZipOutline} size={1} />}
|
|
label="ZIP"
|
|
description={
|
|
<>
|
|
Perform an exhaustive export or import (of accounts, inbox,
|
|
movements and files).
|
|
</>
|
|
}
|
|
importWarning="Existing data will be COMPLETELY ERASED, before starting import!"
|
|
exportURL={BackupApi.ZIPExportURL}
|
|
onImport={(f) => BackupApi.ZIPImport(f)}
|
|
acceptedFiles={"application/zip"}
|
|
/>
|
|
|
|
{/* 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" }}
|
|
>
|
|
FinancesManager
|
|
</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={(f) => BackupApi.FinancesManagerImport(f)}
|
|
/>
|
|
|
|
{/* Excel */}
|
|
<ImportExportModal
|
|
icon={<img src={excelIcon} width={"25em"} />}
|
|
label="Excel"
|
|
description={"Export data in Excel format"}
|
|
exportURL={BackupApi.ExcelExportURL()}
|
|
/>
|
|
</Grid>
|
|
</MoneyMgrWebRouteContainer>
|
|
);
|
|
}
|
|
|
|
function ImportExportModal(p: {
|
|
icon: React.ReactElement;
|
|
label: string;
|
|
description: string | React.ReactElement;
|
|
importWarning?: string;
|
|
exportURL: string;
|
|
onImport?: (file: File) => Promise<void>;
|
|
acceptedFiles?: string;
|
|
}): 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";
|
|
if (p.acceptedFiles) fileEl.accept = p.acceptedFiles;
|
|
fileEl.click();
|
|
|
|
// Wait for a file to be chosen
|
|
await new Promise((res) => {
|
|
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={{ sm: 12, md: 6, lg: 4 }}
|
|
style={{ height: "100%", maxHeight: "250px" }}
|
|
>
|
|
<Card
|
|
elevation={3}
|
|
variant="outlined"
|
|
style={{
|
|
height: "100%",
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
}}
|
|
>
|
|
<CardHeader avatar={p.icon} title={p.label} />
|
|
<CardContent style={{ flex: 1 }}>
|
|
<Typography style={{ textAlign: "justify" }}>
|
|
{p.description}
|
|
</Typography>
|
|
</CardContent>{" "}
|
|
<CardActions>
|
|
<span style={{ flex: 1 }}>
|
|
<RouterLink to={p.exportURL} target="_blank">
|
|
<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>
|
|
);
|
|
}
|