Can import family data from UI
This commit is contained in:
parent
6c82104cdc
commit
4b0292f0a4
@ -14,4 +14,18 @@ export class DataApi {
|
|||||||
});
|
});
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import the data of a family
|
||||||
|
*/
|
||||||
|
static async ImportData(family_id: number, archive: Blob): Promise<Blob> {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.append("archive", archive);
|
||||||
|
const res = await APIClient.exec({
|
||||||
|
uri: `/family/${family_id}/data/import`,
|
||||||
|
method: "PUT",
|
||||||
|
formData: fd,
|
||||||
|
});
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
|
import DownloadIcon from "@mui/icons-material/Download";
|
||||||
|
import UploadIcon from "@mui/icons-material/Upload";
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
|
||||||
CardActions,
|
CardActions,
|
||||||
CardContent,
|
CardContent,
|
||||||
TextField,
|
TextField,
|
||||||
@ -10,17 +11,16 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { DataApi } from "../../api/DataApi";
|
||||||
import { FamilyApi } from "../../api/FamilyApi";
|
import { FamilyApi } from "../../api/FamilyApi";
|
||||||
import { ServerApi } from "../../api/ServerApi";
|
import { ServerApi } from "../../api/ServerApi";
|
||||||
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
|
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
|
||||||
import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
|
import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
|
||||||
import { useFamily } from "../../widgets/BaseFamilyRoute";
|
|
||||||
import { formatDate } from "../../widgets/TimeWidget";
|
|
||||||
import { FamilyCard } from "../../widgets/FamilyCard";
|
|
||||||
import DownloadIcon from "@mui/icons-material/Download";
|
|
||||||
import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider";
|
import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider";
|
||||||
import { DataApi } from "../../api/DataApi";
|
import { downloadBlob, selectFileToUpload } from "../../utils/files_utils";
|
||||||
import { downloadBlob } from "../../utils/blob_utils";
|
import { useFamily } from "../../widgets/BaseFamilyRoute";
|
||||||
|
import { FamilyCard } from "../../widgets/FamilyCard";
|
||||||
|
import { formatDate } from "../../widgets/TimeWidget";
|
||||||
|
|
||||||
export function FamilySettingsRoute(): React.ReactElement {
|
export function FamilySettingsRoute(): React.ReactElement {
|
||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
@ -154,6 +154,7 @@ function FamilySettingsCard(): React.ReactElement {
|
|||||||
|
|
||||||
function FamilyExportCard(): React.ReactElement {
|
function FamilyExportCard(): React.ReactElement {
|
||||||
const loading = useLoadingMessage();
|
const loading = useLoadingMessage();
|
||||||
|
const confirm = useConfirm();
|
||||||
const alert = useAlert();
|
const alert = useAlert();
|
||||||
|
|
||||||
const family = useFamily();
|
const family = useFamily();
|
||||||
@ -169,6 +170,8 @@ function FamilyExportCard(): React.ReactElement {
|
|||||||
|
|
||||||
const blob = await DataApi.ExportData(family.familyId);
|
const blob = await DataApi.ExportData(family.familyId);
|
||||||
downloadBlob(blob, `Export-${new Date().getTime()}.zip`);
|
downloadBlob(blob, `Export-${new Date().getTime()}.zip`);
|
||||||
|
|
||||||
|
setSuccess("Export des données effectué avec succès !");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
setError("Echec de l'export des données de la famille !");
|
setError("Echec de l'export des données de la famille !");
|
||||||
@ -176,6 +179,35 @@ function FamilyExportCard(): React.ReactElement {
|
|||||||
loading.hide();
|
loading.hide();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const importData = async () => {
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
!(await confirm(
|
||||||
|
"Attention ! Cette opération a pour effet d'effacer toutes les données existantes en base ! Voulez-vous vraiment poursuivre l'opération ?"
|
||||||
|
))
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const file = await selectFileToUpload({
|
||||||
|
allowedTypes: ["application/zip"],
|
||||||
|
});
|
||||||
|
if (file === null) return;
|
||||||
|
|
||||||
|
loading.show("Restauration des données de la famille en cours...");
|
||||||
|
|
||||||
|
await DataApi.ImportData(family.familyId, file);
|
||||||
|
|
||||||
|
family.reloadFamilyInfo();
|
||||||
|
|
||||||
|
alert("Import des données de la famille effectué avec succès !");
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
setError(`Echec de l'import des données de la famille ! (${e})`);
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.hide();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FamilyCard error={error} success={success}>
|
<FamilyCard error={error} success={success}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@ -201,9 +233,23 @@ function FamilyExportCard(): React.ReactElement {
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
fullWidth
|
fullWidth
|
||||||
onClick={exportData}
|
onClick={exportData}
|
||||||
|
size={"large"}
|
||||||
|
style={{ marginBottom: "10px" }}
|
||||||
>
|
>
|
||||||
Exporter les données de la famille
|
Exporter les données de la famille
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
startIcon={<UploadIcon />}
|
||||||
|
variant="outlined"
|
||||||
|
color="warning"
|
||||||
|
fullWidth
|
||||||
|
onClick={importData}
|
||||||
|
disabled={!family.family.is_admin}
|
||||||
|
size={"large"}
|
||||||
|
>
|
||||||
|
Importer les données de la famille
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</FamilyCard>
|
</FamilyCard>
|
||||||
);
|
);
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
export async function downloadBlob(blob: Blob, filename: string) {
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
const link = document.createElement("a");
|
|
||||||
link.href = url;
|
|
||||||
link.target = "_blank";
|
|
||||||
link.rel = "noopener";
|
|
||||||
link.download = filename;
|
|
||||||
link.click();
|
|
||||||
}
|
|
45
geneit_app/src/utils/files_utils.ts
Normal file
45
geneit_app/src/utils/files_utils.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { filesize } from "filesize";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a file to upload
|
||||||
|
*/
|
||||||
|
export async function selectFileToUpload(p: {
|
||||||
|
allowedTypes: string[];
|
||||||
|
maxSize?: number;
|
||||||
|
}): Promise<Blob | null> {
|
||||||
|
// Create file element
|
||||||
|
const fileEl = document.createElement("input");
|
||||||
|
fileEl.type = "file";
|
||||||
|
if (p.allowedTypes) fileEl.accept = p.allowedTypes.join(",");
|
||||||
|
fileEl.click();
|
||||||
|
|
||||||
|
// Wait for a file to be chosen
|
||||||
|
await new Promise((res, _rej) =>
|
||||||
|
fileEl.addEventListener("change", () => res(null))
|
||||||
|
);
|
||||||
|
|
||||||
|
if ((fileEl.files?.length ?? 0) === 0) return null;
|
||||||
|
const file = fileEl.files![0];
|
||||||
|
|
||||||
|
// Check file size
|
||||||
|
if (p.maxSize && file.size > p.maxSize) {
|
||||||
|
throw new Error(
|
||||||
|
`Le fichier sélectionné est trop lourd ! (taille maximale acceptée : ${filesize(
|
||||||
|
p.maxSize
|
||||||
|
)})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downloadBlob(blob: Blob, filename: string) {
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = url;
|
||||||
|
link.target = "_blank";
|
||||||
|
link.rel = "noopener";
|
||||||
|
link.download = filename;
|
||||||
|
link.click();
|
||||||
|
}
|
@ -9,6 +9,7 @@ import getCroppedImg from "../../utils/crop_image";
|
|||||||
import UploadIcon from "@mui/icons-material/Upload";
|
import UploadIcon from "@mui/icons-material/Upload";
|
||||||
import LinkIcon from "@mui/icons-material/Link";
|
import LinkIcon from "@mui/icons-material/Link";
|
||||||
import { isDebug } from "../../utils/debug_utils";
|
import { isDebug } from "../../utils/debug_utils";
|
||||||
|
import { selectFileToUpload } from "../../utils/files_utils";
|
||||||
|
|
||||||
export function UploadPhotoButton(p: {
|
export function UploadPhotoButton(p: {
|
||||||
label: string;
|
label: string;
|
||||||
@ -22,39 +23,20 @@ export function UploadPhotoButton(p: {
|
|||||||
|
|
||||||
const uploadPhoto = async () => {
|
const uploadPhoto = async () => {
|
||||||
try {
|
try {
|
||||||
// Create file element
|
const file = await selectFileToUpload({
|
||||||
const fileEl = document.createElement("input");
|
allowedTypes: ServerApi.Config.constraints.photo_allowed_types,
|
||||||
fileEl.type = "file";
|
maxSize: ServerApi.Config.constraints.photo_max_size,
|
||||||
fileEl.accept =
|
});
|
||||||
ServerApi.Config.constraints.photo_allowed_types.join(",");
|
|
||||||
fileEl.click();
|
|
||||||
|
|
||||||
// Wait for a file to be chosen
|
if (file === null) return;
|
||||||
await new Promise((res, _rej) =>
|
|
||||||
fileEl.addEventListener("change", () => res(null))
|
|
||||||
);
|
|
||||||
|
|
||||||
if ((fileEl.files?.length ?? 0) === 0) return null;
|
const tempURL = URL.createObjectURL(file);
|
||||||
const file = fileEl.files![0];
|
|
||||||
|
|
||||||
// Check file size
|
|
||||||
if (file.size > ServerApi.Config.constraints.photo_max_size) {
|
|
||||||
await alert(
|
|
||||||
`Le fichier sélectionné est trop lourd ! (taille maximale acceptée : ${filesize(
|
|
||||||
ServerApi.Config.constraints.photo_max_size
|
|
||||||
)})`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tempURL = URL.createObjectURL(fileEl.files![0]);
|
|
||||||
|
|
||||||
setImageBlob(file);
|
setImageBlob(file);
|
||||||
setImageURL(tempURL);
|
setImageURL(tempURL);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert("Failed to upload custom account image!");
|
alert(`Échec de l'envoi de l'image ! (${e})`);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user