diff --git a/geneit_app/src/App.tsx b/geneit_app/src/App.tsx
index 2fdad99..a07dcd1 100644
--- a/geneit_app/src/App.tsx
+++ b/geneit_app/src/App.tsx
@@ -16,26 +16,29 @@ import { NewAccountRoute } from "./routes/auth/NewAccountRoute";
import { OIDCCbRoute } from "./routes/auth/OIDCCbRoute";
import { PasswordForgottenRoute } from "./routes/auth/PasswordForgottenRoute";
import { ResetPasswordRoute } from "./routes/auth/ResetPasswordRoute";
-import { FamilyHomeRoute } from "./routes/family/FamilyHomeRoute";
+import { FamilyHomeRoute } from "./routes/family/genealogy/FamilyHomeRoute";
import {
FamilyCreateMemberRoute,
FamilyEditMemberRoute,
FamilyMemberRoute,
-} from "./routes/family/FamilyMemberRoute";
+} from "./routes/family/genealogy/FamilyMemberRoute";
import { FamilySettingsRoute } from "./routes/family/FamilySettingsRoute";
import { FamilyUsersListRoute } from "./routes/family/FamilyUsersListRoute";
import { BaseAuthenticatedPage } from "./widgets/BaseAuthenticatedPage";
import { BaseFamilyRoute } from "./widgets/BaseFamilyRoute";
import { BaseLoginPage } from "./widgets/BaseLoginpage";
-import { FamilyMembersListRoute } from "./routes/family/FamilyMembersListRoute";
+import { FamilyMembersListRoute } from "./routes/family/genealogy/FamilyMembersListRoute";
import {
FamilyCoupleRoute,
FamilyCreateCoupleRoute,
FamilyEditCoupleRoute,
-} from "./routes/family/FamilyCoupleRoute";
-import { FamilyCouplesListRoute } from "./routes/family/FamilyCouplesListRoute";
-import { FamilyTreeRoute } from "./routes/family/FamilyTreeRoute";
-import { FamilyMemberTreeRoute } from "./routes/family/FamilyMemberTreeRoute";
+} from "./routes/family/genealogy/FamilyCoupleRoute";
+import { FamilyCouplesListRoute } from "./routes/family/genealogy/FamilyCouplesListRoute";
+import { FamilyTreeRoute } from "./routes/family/genealogy/FamilyTreeRoute";
+import { FamilyMemberTreeRoute } from "./routes/family/genealogy/FamilyMemberTreeRoute";
+import { GenealogyHomeRoute } from "./routes/family/genealogy/GenealogyHomeRoute";
+import { BaseGenealogyRoute } from "./widgets/genealogy/BaseGenealogyRoute";
+import { GenalogySettingsRoute } from "./routes/family/genealogy/GenalogySettingsRoute";
interface AuthContext {
signedIn: boolean;
@@ -67,33 +70,45 @@ export function App(): React.ReactElement {
}>
} />
- } />
- }
- />
- } />
- }
- />
+ }>
+ } />
- } />
- }
- />
- } />
- }
- />
+ } />
+ }
+ />
+ }
+ />
+ }
+ />
- } />
- }
- />
+ } />
+ }
+ />
+ }
+ />
+ }
+ />
+
+ } />
+ }
+ />
+ } />
+ } />
+
} />
} />
diff --git a/geneit_app/src/api/FamilyApi.ts b/geneit_app/src/api/FamilyApi.ts
index 1518cac..31d97b6 100644
--- a/geneit_app/src/api/FamilyApi.ts
+++ b/geneit_app/src/api/FamilyApi.ts
@@ -1,6 +1,6 @@
import { APIClient } from "./ApiClient";
-import { Couple } from "./CoupleApi";
-import { Member } from "./MemberApi";
+import { Couple } from "./genealogy/CoupleApi";
+import { Member } from "./genealogy/MemberApi";
interface FamilyAPI {
user_id: number;
@@ -60,7 +60,8 @@ export class Family implements FamilyAPI {
*/
memberURL(member: Member, edit?: boolean): string {
return (
- `/family/${this.family_id}/member/${member.id}` + (edit ? "/edit" : "")
+ `/family/${this.family_id}/genealogy/member/${member.id}` +
+ (edit ? "/edit" : "")
);
}
@@ -68,7 +69,7 @@ export class Family implements FamilyAPI {
* Get family tree URL for member
*/
familyTreeURL(member: Member | number): string {
- return `/family/${this.family_id}/tree/${
+ return `/family/${this.family_id}/genealogy/tree/${
typeof member === "number" ? member : member.id
}`;
}
@@ -78,16 +79,19 @@ export class Family implements FamilyAPI {
*/
coupleURL(member: Couple, edit?: boolean): string {
return (
- `/family/${this.family_id}/couple/${member.id}` + (edit ? "/edit" : "")
+ `/family/${this.family_id}/genealogy/couple/${member.id}` +
+ (edit ? "/edit" : "")
);
}
}
export class ExtendedFamilyInfo extends Family {
public disable_couple_photos: boolean;
+ public enable_genealogy: boolean;
constructor(p: any) {
super(p);
this.disable_couple_photos = p.disable_couple_photos;
+ this.enable_genealogy = p.enable_genealogy;
}
}
@@ -229,14 +233,16 @@ export class FamilyApi {
*/
static async UpdateFamily(settings: {
id: number;
- name: string;
- disable_couple_photos: boolean;
+ name?: string;
+ enable_genealogy?: boolean;
+ disable_couple_photos?: boolean;
}): Promise {
await APIClient.exec({
method: "PATCH",
uri: `/family/${settings.id}`,
jsonData: {
name: settings.name,
+ enable_genealogy: settings.enable_genealogy,
disable_couple_photos: settings.disable_couple_photos,
},
});
diff --git a/geneit_app/src/api/CoupleApi.ts b/geneit_app/src/api/genealogy/CoupleApi.ts
similarity index 90%
rename from geneit_app/src/api/CoupleApi.ts
rename to geneit_app/src/api/genealogy/CoupleApi.ts
index 41234bd..bb12ae3 100644
--- a/geneit_app/src/api/CoupleApi.ts
+++ b/geneit_app/src/api/genealogy/CoupleApi.ts
@@ -1,6 +1,6 @@
-import { APIClient } from "./ApiClient";
+import { APIClient } from "../ApiClient";
import { DateValue, Member } from "./MemberApi";
-import { ServerApi } from "./ServerApi";
+import { ServerApi } from "../ServerApi";
interface CoupleApiInterface {
id: number;
@@ -161,7 +161,7 @@ export class CoupleApi {
*/
static async Create(m: Couple): Promise {
const res = await APIClient.exec({
- uri: `/family/${m.family_id}/couple/create`,
+ uri: `/family/${m.family_id}/genealogy/couple/create`,
method: "POST",
jsonData: m,
});
@@ -177,7 +177,7 @@ export class CoupleApi {
couple_id: number
): Promise {
const res = await APIClient.exec({
- uri: `/family/${family_id}/couple/${couple_id}`,
+ uri: `/family/${family_id}/genealogy/couple/${couple_id}`,
method: "GET",
});
@@ -189,7 +189,7 @@ export class CoupleApi {
*/
static async GetEntireList(family_id: number): Promise {
const res = await APIClient.exec({
- uri: `/family/${family_id}/couples`,
+ uri: `/family/${family_id}/genealogy/couples`,
method: "GET",
});
@@ -201,7 +201,7 @@ export class CoupleApi {
*/
static async Update(m: Couple): Promise {
await APIClient.exec({
- uri: `/family/${m.family_id}/couple/${m.id}`,
+ uri: `/family/${m.family_id}/genealogy/couple/${m.id}`,
method: "PUT",
jsonData: m,
});
@@ -214,7 +214,7 @@ export class CoupleApi {
const fd = new FormData();
fd.append("photo", b);
await APIClient.exec({
- uri: `/family/${m.family_id}/couple/${m.id}/photo`,
+ uri: `/family/${m.family_id}/genealogy/couple/${m.id}/photo`,
method: "PUT",
formData: fd,
});
@@ -225,7 +225,7 @@ export class CoupleApi {
*/
static async RemoveCouplePhoto(m: Couple): Promise {
await APIClient.exec({
- uri: `/family/${m.family_id}/couple/${m.id}/photo`,
+ uri: `/family/${m.family_id}/genealogy/couple/${m.id}/photo`,
method: "DELETE",
});
}
@@ -235,7 +235,7 @@ export class CoupleApi {
*/
static async Delete(m: Couple): Promise {
await APIClient.exec({
- uri: `/family/${m.family_id}/couple/${m.id}`,
+ uri: `/family/${m.family_id}/genealogy/couple/${m.id}`,
method: "DELETE",
});
}
diff --git a/geneit_app/src/api/DataApi.ts b/geneit_app/src/api/genealogy/DataApi.ts
similarity index 78%
rename from geneit_app/src/api/DataApi.ts
rename to geneit_app/src/api/genealogy/DataApi.ts
index 2b9a735..2c5281d 100644
--- a/geneit_app/src/api/DataApi.ts
+++ b/geneit_app/src/api/genealogy/DataApi.ts
@@ -1,4 +1,4 @@
-import { APIClient } from "./ApiClient";
+import { APIClient } from "../ApiClient";
/**
* Data management api client
@@ -9,7 +9,7 @@ export class DataApi {
*/
static async ExportData(family_id: number): Promise {
const res = await APIClient.exec({
- uri: `/family/${family_id}/data/export`,
+ uri: `/family/${family_id}/genealogy/data/export`,
method: "GET",
});
return res.data;
@@ -22,7 +22,7 @@ export class DataApi {
const fd = new FormData();
fd.append("archive", archive);
const res = await APIClient.exec({
- uri: `/family/${family_id}/data/import`,
+ uri: `/family/${family_id}/genealogy/data/import`,
method: "PUT",
formData: fd,
});
diff --git a/geneit_app/src/api/MemberApi.ts b/geneit_app/src/api/genealogy/MemberApi.ts
similarity index 94%
rename from geneit_app/src/api/MemberApi.ts
rename to geneit_app/src/api/genealogy/MemberApi.ts
index 66b0d73..db989ab 100644
--- a/geneit_app/src/api/MemberApi.ts
+++ b/geneit_app/src/api/genealogy/MemberApi.ts
@@ -1,4 +1,4 @@
-import { APIClient } from "./ApiClient";
+import { APIClient } from "../ApiClient";
import { Couple } from "./CoupleApi";
export type Sex = "M" | "F";
@@ -278,7 +278,7 @@ export class MemberApi {
*/
static async Create(m: Member): Promise {
const res = await APIClient.exec({
- uri: `/family/${m.family_id}/member/create`,
+ uri: `/family/${m.family_id}/genealogy/member/create`,
method: "POST",
jsonData: m,
});
@@ -294,7 +294,7 @@ export class MemberApi {
member_id: number
): Promise {
const res = await APIClient.exec({
- uri: `/family/${family_id}/member/${member_id}`,
+ uri: `/family/${family_id}/genealogy/member/${member_id}`,
method: "GET",
});
@@ -306,7 +306,7 @@ export class MemberApi {
*/
static async GetEntireList(family_id: number): Promise {
const res = await APIClient.exec({
- uri: `/family/${family_id}/members`,
+ uri: `/family/${family_id}/genealogy/members`,
method: "GET",
});
@@ -318,7 +318,7 @@ export class MemberApi {
*/
static async Update(m: Member): Promise {
await APIClient.exec({
- uri: `/family/${m.family_id}/member/${m.id}`,
+ uri: `/family/${m.family_id}/genealogy/member/${m.id}`,
method: "PUT",
jsonData: m,
});
@@ -331,7 +331,7 @@ export class MemberApi {
const fd = new FormData();
fd.append("photo", b);
await APIClient.exec({
- uri: `/family/${m.family_id}/member/${m.id}/photo`,
+ uri: `/family/${m.family_id}/genealogy/member/${m.id}/photo`,
method: "PUT",
formData: fd,
});
@@ -342,7 +342,7 @@ export class MemberApi {
*/
static async RemoveMemberPhoto(m: Member): Promise {
await APIClient.exec({
- uri: `/family/${m.family_id}/member/${m.id}/photo`,
+ uri: `/family/${m.family_id}/genealogy/member/${m.id}/photo`,
method: "DELETE",
});
}
@@ -352,7 +352,7 @@ export class MemberApi {
*/
static async Delete(m: Member): Promise {
await APIClient.exec({
- uri: `/family/${m.family_id}/member/${m.id}`,
+ uri: `/family/${m.family_id}/genealogy/member/${m.id}`,
method: "DELETE",
});
}
diff --git a/geneit_app/src/routes/family/FamilyHomeRoute.tsx b/geneit_app/src/routes/family/FamilyHomeRoute.tsx
deleted file mode 100644
index eaa5364..0000000
--- a/geneit_app/src/routes/family/FamilyHomeRoute.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useFamily } from "../../widgets/BaseFamilyRoute";
-import { FamilyPageTitle } from "../../widgets/FamilyPageTitle";
-
-export function FamilyHomeRoute(): React.ReactElement {
- const family = useFamily();
- return (
- <>
-
-
-
- Bienvenue sur l'espace informatique dédié à la vie de votre famille !
- Veuillez utiliser le menu situé à gauche pour accéder aux différentes
- sections de l'application.
-
-
Nombre de fiches de membres: {family.members.size}
-
Nombre de fiches de couples: {family.couples.size}
-
- Vous pouvez inviter d'autres personnes à rejoindre cette famille en
- leur donnant une copie du code d'invitation
-
-
- >
- );
-}
diff --git a/geneit_app/src/routes/family/FamilySettingsRoute.tsx b/geneit_app/src/routes/family/FamilySettingsRoute.tsx
index 9f98d70..7c0d720 100644
--- a/geneit_app/src/routes/family/FamilySettingsRoute.tsx
+++ b/geneit_app/src/routes/family/FamilySettingsRoute.tsx
@@ -1,26 +1,19 @@
-import DownloadIcon from "@mui/icons-material/Download";
-import UploadIcon from "@mui/icons-material/Upload";
import {
- Alert,
Box,
Button,
CardActions,
CardContent,
- Checkbox,
FormControlLabel,
+ Switch,
TextField,
- Tooltip,
Typography,
} from "@mui/material";
import React from "react";
import { useNavigate } from "react-router-dom";
-import { DataApi } from "../../api/DataApi";
import { FamilyApi } from "../../api/FamilyApi";
import { ServerApi } from "../../api/ServerApi";
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
-import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider";
-import { downloadBlob, selectFileToUpload } from "../../utils/files_utils";
import { useFamily } from "../../widgets/BaseFamilyRoute";
import { FamilyCard } from "../../widgets/FamilyCard";
import { formatDate } from "../../widgets/TimeWidget";
@@ -55,7 +48,6 @@ export function FamilySettingsRoute(): React.ReactElement {
return (
<>
-
-
- setDisableCouplePhotos(c)}
- />
- }
- label="Désactiver les photos de couple"
- />
-
+
+ setEnableGenealogy(c)}
+ />
+ }
+ label="Activer le module de généalogie"
+ />
@@ -170,109 +161,3 @@ function FamilySettingsCard(): React.ReactElement {
);
}
-
-function FamilyExportCard(): React.ReactElement {
- const loading = useLoadingMessage();
- const confirm = useConfirm();
- const alert = useAlert();
-
- const family = useFamily();
-
- const [error, setError] = React.useState();
- const [success, setSuccess] = React.useState();
-
- const exportData = async () => {
- loading.show("Export des données");
- try {
- setError(undefined);
- setSuccess(undefined);
-
- const blob = await DataApi.ExportData(family.familyId);
- downloadBlob(blob, `Export-${new Date().getTime()}.zip`);
-
- setSuccess("Export des données effectué avec succès !");
- } catch (e) {
- console.error(e);
- setError("Echec de l'export des données de la famille !");
- }
- 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;
-
- setError(undefined);
- setSuccess(undefined);
-
- 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 (
-
-
-
- Export / import des données de la famille
-
-
- Vous pouvez, à des fins de sauvegardes ou de transfert, exporter et
- importer l'ensemble des données des membres et des couples de cette
- famille, sous format ZIP.
-
-
-
- Attention ! La restauration des données de la famille provoque
- préalablement l'effacement de toutes les données enregistrées dans la
- famille ! Par ailleurs, la restauration n'est pas réversible !
-
-
-
-
- }
- variant="outlined"
- fullWidth
- onClick={exportData}
- size={"large"}
- style={{ marginBottom: "10px" }}
- >
- Exporter les données de la famille
-
-
- }
- variant="outlined"
- color="warning"
- fullWidth
- onClick={importData}
- disabled={!family.family.is_admin}
- size={"large"}
- >
- Importer les données de la famille
-
-
-
- );
-}
diff --git a/geneit_app/src/routes/family/FamilyCoupleRoute.tsx b/geneit_app/src/routes/family/genealogy/FamilyCoupleRoute.tsx
similarity index 86%
rename from geneit_app/src/routes/family/FamilyCoupleRoute.tsx
rename to geneit_app/src/routes/family/genealogy/FamilyCoupleRoute.tsx
index 172e870..19e8f50 100644
--- a/geneit_app/src/routes/family/FamilyCoupleRoute.tsx
+++ b/geneit_app/src/routes/family/genealogy/FamilyCoupleRoute.tsx
@@ -6,26 +6,27 @@ import SaveIcon from "@mui/icons-material/Save";
import { Button, Grid, Stack } from "@mui/material";
import React from "react";
import { useNavigate, useParams } from "react-router-dom";
-import { Couple, CoupleApi } from "../../api/CoupleApi";
-import { Member } from "../../api/MemberApi";
-import { ServerApi } from "../../api/ServerApi";
-import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
-import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
-import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
-import { AsyncWidget } from "../../widgets/AsyncWidget";
-import { useFamily } from "../../widgets/BaseFamilyRoute";
-import { ConfirmLeaveWithoutSaveDialog } from "../../widgets/ConfirmLeaveWithoutSaveDialog";
-import { CouplePhoto } from "../../widgets/CouplePhoto";
-import { FamilyPageTitle } from "../../widgets/FamilyPageTitle";
-import { MemberItem } from "../../widgets/MemberItem";
-import { PropertiesBox } from "../../widgets/PropertiesBox";
-import { RouterLink } from "../../widgets/RouterLink";
-import { DateInput } from "../../widgets/forms/DateInput";
-import { MemberInput } from "../../widgets/forms/MemberInput";
-import { PropSelect } from "../../widgets/forms/PropSelect";
-import { UploadPhotoButton } from "../../widgets/forms/UploadPhotoButton";
-import { useQuery } from "../../hooks/useQuery";
-import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider";
+import { ServerApi } from "../../../api/ServerApi";
+import { Couple, CoupleApi } from "../../../api/genealogy/CoupleApi";
+import { Member } from "../../../api/genealogy/MemberApi";
+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 { useQuery } from "../../../hooks/useQuery";
+import { AsyncWidget } from "../../../widgets/AsyncWidget";
+import { useFamily } from "../../../widgets/BaseFamilyRoute";
+import { ConfirmLeaveWithoutSaveDialog } from "../../../widgets/ConfirmLeaveWithoutSaveDialog";
+import { CouplePhoto } from "../../../widgets/CouplePhoto";
+import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle";
+import { MemberItem } from "../../../widgets/MemberItem";
+import { PropertiesBox } from "../../../widgets/PropertiesBox";
+import { RouterLink } from "../../../widgets/RouterLink";
+import { DateInput } from "../../../widgets/forms/DateInput";
+import { MemberInput } from "../../../widgets/forms/MemberInput";
+import { PropSelect } from "../../../widgets/forms/PropSelect";
+import { UploadPhotoButton } from "../../../widgets/forms/UploadPhotoButton";
+import { useGenealogy } from "../../../widgets/genealogy/BaseGenealogyRoute";
/**
* Create a new couple route
@@ -36,6 +37,7 @@ export function FamilyCreateCoupleRoute(): React.ReactElement {
const [shouldQuit, setShouldQuit] = React.useState(false);
const n = useNavigate();
+ const genealogy = useGenealogy();
const family = useFamily();
const params = useQuery();
@@ -49,7 +51,7 @@ export function FamilyCreateCoupleRoute(): React.ReactElement {
try {
const r = await CoupleApi.Create(m);
- await family.reloadCouplesList();
+ await genealogy.reloadCouplesList();
setShouldQuit(true);
n(family.family.coupleURL(r));
@@ -62,7 +64,7 @@ export function FamilyCreateCoupleRoute(): React.ReactElement {
const cancel = () => {
setShouldQuit(true);
- n(family.family.URL("couples"));
+ n(family.family.URL("genealogy/couples"));
};
return (
@@ -89,6 +91,7 @@ export function FamilyCoupleRoute(): React.ReactElement {
const snackbar = useSnackbar();
const family = useFamily();
+ const genealogy = useGenealogy();
const { coupleId } = useParams();
const [couple, setCouple] = React.useState();
@@ -100,7 +103,7 @@ export function FamilyCoupleRoute(): React.ReactElement {
count.current += 1;
setCouple(undefined);
- await family.reloadCouplesList();
+ await genealogy.reloadCouplesList();
};
const deleteCouple = async () => {
@@ -115,9 +118,9 @@ export function FamilyCoupleRoute(): React.ReactElement {
await CoupleApi.Delete(couple!);
snackbar("La fiche du couple a été supprimée avec succès !");
- n(family.family.URL("couples"));
+ n(family.family.URL("genealogy/couples"));
- await family.reloadCouplesList();
+ await genealogy.reloadCouplesList();
} catch (e) {
console.error(e);
alert("Échec de la suppression du couple !");
@@ -133,7 +136,7 @@ export function FamilyCoupleRoute(): React.ReactElement {
build={() => (
();
@@ -176,7 +180,7 @@ export function FamilyEditCoupleRoute(): React.ReactElement {
snackbar("Les informations du couple ont été mises à jour avec succès !");
- await family.reloadCouplesList();
+ await genealogy.reloadCouplesList();
setShouldQuit(true);
n(family.family.coupleURL(c, false));
@@ -486,7 +490,7 @@ export function CouplePage(p: {
diff --git a/geneit_app/src/routes/family/FamilyCouplesListRoute.tsx b/geneit_app/src/routes/family/genealogy/FamilyCouplesListRoute.tsx
similarity index 80%
rename from geneit_app/src/routes/family/FamilyCouplesListRoute.tsx
rename to geneit_app/src/routes/family/genealogy/FamilyCouplesListRoute.tsx
index c34c1f1..465f191 100644
--- a/geneit_app/src/routes/family/FamilyCouplesListRoute.tsx
+++ b/geneit_app/src/routes/family/genealogy/FamilyCouplesListRoute.tsx
@@ -6,17 +6,18 @@ import { Button, TextField, Tooltip } from "@mui/material";
import { DataGrid, GridActionsCellItem, GridColDef } from "@mui/x-data-grid";
import React from "react";
import { useNavigate } from "react-router-dom";
-import { Couple, CoupleApi } from "../../api/CoupleApi";
-import { dateTimestamp, fmtDate } from "../../api/MemberApi";
-import { ServerApi } from "../../api/ServerApi";
-import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
-import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
-import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
-import { useFamily } from "../../widgets/BaseFamilyRoute";
-import { CouplePhoto } from "../../widgets/CouplePhoto";
-import { FamilyPageTitle } from "../../widgets/FamilyPageTitle";
-import { MemberPhoto } from "../../widgets/MemberPhoto";
-import { RouterLink } from "../../widgets/RouterLink";
+import { Couple, CoupleApi } from "../../../api/genealogy/CoupleApi";
+import { dateTimestamp, fmtDate } from "../../../api/genealogy/MemberApi";
+import { ServerApi } from "../../../api/ServerApi";
+import { useAlert } from "../../../hooks/context_providers/AlertDialogProvider";
+import { useConfirm } from "../../../hooks/context_providers/ConfirmDialogProvider";
+import { useSnackbar } from "../../../hooks/context_providers/SnackbarProvider";
+import { useFamily } from "../../../widgets/BaseFamilyRoute";
+import { CouplePhoto } from "../../../widgets/CouplePhoto";
+import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle";
+import { MemberPhoto } from "../../../widgets/MemberPhoto";
+import { RouterLink } from "../../../widgets/RouterLink";
+import { useGenealogy } from "../../../widgets/genealogy/BaseGenealogyRoute";
export function FamilyCouplesListRoute(): React.ReactElement {
const alert = useAlert();
@@ -24,6 +25,7 @@ export function FamilyCouplesListRoute(): React.ReactElement {
const snackbar = useSnackbar();
const family = useFamily();
+ const genealogy = useGenealogy();
const [filter, setFilter] = React.useState("");
@@ -37,7 +39,7 @@ export function FamilyCouplesListRoute(): React.ReactElement {
return;
await CoupleApi.Delete(c);
- await family.reloadCouplesList();
+ await genealogy.reloadCouplesList();
snackbar("La fiche du couple a été supprimée avec succès !");
} catch (e) {
@@ -63,7 +65,7 @@ export function FamilyCouplesListRoute(): React.ReactElement {
Votre famille n'a aucun couple enregistré pour le moment ! Utilisez le
bouton situé en haut à droite pour créer le premier !
@@ -81,16 +83,16 @@ export function FamilyCouplesListRoute(): React.ReactElement {
(m.wife &&
- family.members
+ genealogy.members
.get(m.wife)!
.fullName.toLocaleLowerCase()
.includes(filter.toLocaleLowerCase())) ||
(m.husband &&
- family.members
+ genealogy.members
.get(m.husband)!
.fullName.toLocaleLowerCase()
.includes(filter.toLocaleLowerCase())) === true
@@ -109,14 +111,18 @@ function CouplesTable(p: {
onDelete: (m: Couple) => void;
}): React.ReactElement {
const family = useFamily();
+ const genealogy = useGenealogy();
+
const n = useNavigate();
const compareInvertedMembersNames = (
v1: number | undefined,
v2: number | undefined
) => {
- const n1 = ((v1 && family.members.get(v1)?.invertedFullName) ?? "") || "";
- const n2 = ((v2 && family.members.get(v2)?.invertedFullName) ?? "") || "";
+ const n1 =
+ ((v1 && genealogy.members.get(v1)?.invertedFullName) ?? "") || "";
+ const n2 =
+ ((v2 && genealogy.members.get(v2)?.invertedFullName) ?? "") || "";
return n1?.localeCompare(n2, undefined, {
ignorePunctuation: true,
@@ -132,7 +138,13 @@ function CouplesTable(p: {
sortable: false,
width: 60,
renderCell(params) {
- return ;
+ return (
+
+
+
+ );
},
},
@@ -253,10 +265,10 @@ function CouplesTable(p: {
}
function MemberCell(p: { id?: number }): React.ReactElement {
- const family = useFamily();
+ const genealogy = useGenealogy();
if (!p.id) return <>>;
- const member = family.members.get(p.id!)!;
+ const member = genealogy.members.get(p.id!)!;
return (
diff --git a/geneit_app/src/routes/family/genealogy/FamilyHomeRoute.tsx b/geneit_app/src/routes/family/genealogy/FamilyHomeRoute.tsx
new file mode 100644
index 0000000..4a66382
--- /dev/null
+++ b/geneit_app/src/routes/family/genealogy/FamilyHomeRoute.tsx
@@ -0,0 +1,17 @@
+import { useFamily } from "../../../widgets/BaseFamilyRoute";
+import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle";
+
+export function FamilyHomeRoute(): React.ReactElement {
+ return (
+ <>
+
+
+
+ Bienvenue sur l'espace informatique dédié à la vie de votre famille !
+ Veuillez utiliser le menu situé à gauche pour accéder aux différentes
+ sections de l'application.
+
+
+ >
+ );
+}
diff --git a/geneit_app/src/routes/family/FamilyMemberRoute.tsx b/geneit_app/src/routes/family/genealogy/FamilyMemberRoute.tsx
similarity index 87%
rename from geneit_app/src/routes/family/FamilyMemberRoute.tsx
rename to geneit_app/src/routes/family/genealogy/FamilyMemberRoute.tsx
index 7502189..5a5fa01 100644
--- a/geneit_app/src/routes/family/FamilyMemberRoute.tsx
+++ b/geneit_app/src/routes/family/genealogy/FamilyMemberRoute.tsx
@@ -1,3 +1,5 @@
+import { mdiFamilyTree } from "@mdi/js";
+import Icon from "@mdi/react";
import ClearIcon from "@mui/icons-material/Clear";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
@@ -14,32 +16,31 @@ import {
import * as EmailValidator from "email-validator";
import React from "react";
import { useNavigate, useParams } from "react-router-dom";
-import { Couple } from "../../api/CoupleApi";
-import { Member, MemberApi, fmtDate } from "../../api/MemberApi";
-import { ServerApi } from "../../api/ServerApi";
-import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
-import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
-import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
-import { AsyncWidget } from "../../widgets/AsyncWidget";
-import { useFamily } from "../../widgets/BaseFamilyRoute";
-import { ConfirmLeaveWithoutSaveDialog } from "../../widgets/ConfirmLeaveWithoutSaveDialog";
-import { CouplePhoto } from "../../widgets/CouplePhoto";
-import { FamilyPageTitle } from "../../widgets/FamilyPageTitle";
-import { MemberItem } from "../../widgets/MemberItem";
-import { MemberPhoto } from "../../widgets/MemberPhoto";
-import { PropertiesBox } from "../../widgets/PropertiesBox";
-import { RouterLink } from "../../widgets/RouterLink";
-import { DateInput } from "../../widgets/forms/DateInput";
-import { MemberInput } from "../../widgets/forms/MemberInput";
-import { PropCheckbox } from "../../widgets/forms/PropCheckbox";
-import { PropEdit } from "../../widgets/forms/PropEdit";
-import { PropSelect } from "../../widgets/forms/PropSelect";
-import { SexSelection } from "../../widgets/forms/SexSelection";
-import { UploadPhotoButton } from "../../widgets/forms/UploadPhotoButton";
-import { useQuery } from "../../hooks/useQuery";
-import { mdiFamilyTree } from "@mdi/js";
-import Icon from "@mdi/react";
-import { useLoadingMessage } from "../../hooks/context_providers/LoadingMessageProvider";
+import { ServerApi } from "../../../api/ServerApi";
+import { Couple } from "../../../api/genealogy/CoupleApi";
+import { Member, MemberApi, fmtDate } from "../../../api/genealogy/MemberApi";
+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 { useQuery } from "../../../hooks/useQuery";
+import { AsyncWidget } from "../../../widgets/AsyncWidget";
+import { useFamily } from "../../../widgets/BaseFamilyRoute";
+import { ConfirmLeaveWithoutSaveDialog } from "../../../widgets/ConfirmLeaveWithoutSaveDialog";
+import { CouplePhoto } from "../../../widgets/CouplePhoto";
+import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle";
+import { MemberItem } from "../../../widgets/MemberItem";
+import { MemberPhoto } from "../../../widgets/MemberPhoto";
+import { PropertiesBox } from "../../../widgets/PropertiesBox";
+import { RouterLink } from "../../../widgets/RouterLink";
+import { DateInput } from "../../../widgets/forms/DateInput";
+import { MemberInput } from "../../../widgets/forms/MemberInput";
+import { PropCheckbox } from "../../../widgets/forms/PropCheckbox";
+import { PropEdit } from "../../../widgets/forms/PropEdit";
+import { PropSelect } from "../../../widgets/forms/PropSelect";
+import { SexSelection } from "../../../widgets/forms/SexSelection";
+import { UploadPhotoButton } from "../../../widgets/forms/UploadPhotoButton";
+import { useGenealogy } from "../../../widgets/genealogy/BaseGenealogyRoute";
/**
* Create a new member route
@@ -50,6 +51,7 @@ export function FamilyCreateMemberRoute(): React.ReactElement {
const [shouldQuit, setShouldQuit] = React.useState(false);
const n = useNavigate();
+ const genealogy = useGenealogy();
const family = useFamily();
const parameters = useQuery();
@@ -60,10 +62,10 @@ export function FamilyCreateMemberRoute(): React.ReactElement {
try {
const r = await MemberApi.Create(m);
- await family.reloadMembersList();
+ await genealogy.reloadMembersList();
setShouldQuit(true);
- n(family.family.URL(`member/${r.id}`));
+ n(family.family.URL(`genealogy/member/${r.id}`));
snackbar(`La fiche pour ${r.fullName} a été créée avec succès !`);
} catch (e) {
console.error(e);
@@ -73,7 +75,7 @@ export function FamilyCreateMemberRoute(): React.ReactElement {
const cancel = () => {
setShouldQuit(true);
- n(family.family.URL("members"));
+ n(family.family.URL("genealogy/members"));
};
const member = Member.New(family.family.family_id);
@@ -104,6 +106,7 @@ export function FamilyMemberRoute(): React.ReactElement {
const snackbar = useSnackbar();
const family = useFamily();
+ const genealogy = useGenealogy();
const { memberId } = useParams();
const [member, setMember] = React.useState();
@@ -115,7 +118,7 @@ export function FamilyMemberRoute(): React.ReactElement {
count.current += 1;
setMember(undefined);
- await family.reloadMembersList();
+ await genealogy.reloadMembersList();
};
const deleteMember = async () => {
@@ -130,9 +133,9 @@ export function FamilyMemberRoute(): React.ReactElement {
await MemberApi.Delete(member!);
snackbar("La fiche de membre a été supprimée avec succès !");
- n(family.family.URL("members"));
+ n(family.family.URL("genealogy/members"));
- await family.reloadMembersList();
+ await genealogy.reloadMembersList();
} catch (e) {
console.error(e);
alert("Échec de la suppression du membre !");
@@ -148,16 +151,14 @@ export function FamilyMemberRoute(): React.ReactElement {
build={() => (
n(family.family.familyTreeURL(member!))}
onRequestDelete={deleteMember}
- onRequestEdit={() =>
- n(family.family.URL(`member/${member!.id}/edit`))
- }
+ onRequestEdit={() => n(family.family.memberURL(member!, true))}
onForceReload={forceReload}
/>
)}
@@ -178,6 +179,7 @@ export function FamilyEditMemberRoute(): React.ReactElement {
const [shouldQuit, setShouldQuit] = React.useState(false);
const family = useFamily();
+ const genealogy = useGenealogy();
const [member, setMember] = React.useState();
const load = async () => {
@@ -196,10 +198,10 @@ export function FamilyEditMemberRoute(): React.ReactElement {
snackbar("Les informations du membre ont été mises à jour avec succès !");
- await family.reloadMembersList();
+ await genealogy.reloadMembersList();
setShouldQuit(true);
- n(family.family.URL(`member/${member!.id}`));
+ n(family.family.memberURL(member!));
} catch (e) {
console.error(e);
alert("Échec de la mise à jour des informations du membre !");
@@ -662,9 +664,9 @@ export function MemberPage(p: {
Votre famille n'a aucun membre pour le moment ! Utilisez le bouton
situé en haut à droite pour créer le premier !
@@ -80,8 +87,8 @@ export function FamilyMembersListRoute(): React.ReactElement {
+ ? genealogy.members.fullList
+ : genealogy.members.filter((m) =>
m.fullName.toLowerCase().includes(filter.toLowerCase())
)
}
@@ -97,6 +104,7 @@ function MembersTable(p: {
members: Member[];
onDelete: (m: Member) => void;
}): React.ReactElement {
+ const genealogy = useGenealogy();
const family = useFamily();
const n = useNavigate();
@@ -108,7 +116,13 @@ function MembersTable(p: {
sortable: false,
width: 60,
renderCell(params) {
- return ;
+ return (
+
+
+
+ );
},
},
@@ -166,8 +180,12 @@ function MembersTable(p: {
flex: 5,
renderCell(params) {
if (!params.row.father)
- return Non renseigné;
- return family.members.get(params.row.father)!.fullName;
+ return (
+
+ Non renseigné
+
+ );
+ return genealogy.members.get(params.row.father)!.fullName;
},
},
{
@@ -176,8 +194,12 @@ function MembersTable(p: {
flex: 5,
renderCell(params) {
if (!params.row.mother)
- return Non renseignée;
- return family.members.get(params.row.mother)!.fullName;
+ return (
+
+ Non renseignée
+
+ );
+ return genealogy.members.get(params.row.mother)!.fullName;
},
},
{
diff --git a/geneit_app/src/routes/family/FamilyTreeRoute.tsx b/geneit_app/src/routes/family/genealogy/FamilyTreeRoute.tsx
similarity index 87%
rename from geneit_app/src/routes/family/FamilyTreeRoute.tsx
rename to geneit_app/src/routes/family/genealogy/FamilyTreeRoute.tsx
index 86bda38..0e0a98e 100644
--- a/geneit_app/src/routes/family/FamilyTreeRoute.tsx
+++ b/geneit_app/src/routes/family/genealogy/FamilyTreeRoute.tsx
@@ -1,6 +1,6 @@
import { useNavigate } from "react-router-dom";
-import { useFamily } from "../../widgets/BaseFamilyRoute";
-import { MemberInput } from "../../widgets/forms/MemberInput";
+import { useFamily } from "../../../widgets/BaseFamilyRoute";
+import { MemberInput } from "../../../widgets/forms/MemberInput";
export function FamilyTreeRoute(): React.ReactElement {
const n = useNavigate();
diff --git a/geneit_app/src/routes/family/genealogy/GenalogySettingsRoute.tsx b/geneit_app/src/routes/family/genealogy/GenalogySettingsRoute.tsx
new file mode 100644
index 0000000..086ffca
--- /dev/null
+++ b/geneit_app/src/routes/family/genealogy/GenalogySettingsRoute.tsx
@@ -0,0 +1,221 @@
+import DownloadIcon from "@mui/icons-material/Download";
+import UploadIcon from "@mui/icons-material/Upload";
+import {
+ Alert,
+ Box,
+ Button,
+ CardActions,
+ CardContent,
+ FormControlLabel,
+ Switch,
+ Tooltip,
+ Typography,
+} from "@mui/material";
+import React from "react";
+import { FamilyApi } from "../../../api/FamilyApi";
+import { DataApi } from "../../../api/genealogy/DataApi";
+import { useAlert } from "../../../hooks/context_providers/AlertDialogProvider";
+import { useConfirm } from "../../../hooks/context_providers/ConfirmDialogProvider";
+import { useLoadingMessage } from "../../../hooks/context_providers/LoadingMessageProvider";
+import { downloadBlob, selectFileToUpload } from "../../../utils/files_utils";
+import { useFamily } from "../../../widgets/BaseFamilyRoute";
+import { FamilyCard } from "../../../widgets/FamilyCard";
+
+export function GenalogySettingsRoute(): React.ReactElement {
+ return (
+ <>
+
+
+ >
+ );
+}
+
+function GenealogySettingsCard(): React.ReactElement {
+ const alert = useAlert();
+
+ const family = useFamily();
+
+ const [disableCouplePhotos, setDisableCouplePhotos] = React.useState(
+ family.family.disable_couple_photos
+ );
+
+ const canEdit = family.family.is_admin;
+
+ const [error, setError] = React.useState();
+ const [success, setSuccess] = React.useState();
+
+ const updateFamily = async () => {
+ try {
+ setError(undefined);
+ setSuccess(undefined);
+
+ await FamilyApi.UpdateFamily({
+ id: family.family.family_id,
+ disable_couple_photos: disableCouplePhotos,
+ });
+
+ family.reloadFamilyInfo();
+
+ alert("Les paramètres de la famille ont été mis à jour avec succès !");
+ } catch (e) {
+ console.error(e);
+ setError("Echec de la mise à jour des paramètres de la famille !");
+ }
+ };
+
+ return (
+
+
+
+ Paramètres du module de généalogie
+
+
+
+
+ setDisableCouplePhotos(c)}
+ />
+ }
+ label="Désactiver les photos de couple"
+ />
+
+
+
+
+
+
+
+ );
+}
+
+function GenealogyExportCard(): React.ReactElement {
+ const loading = useLoadingMessage();
+ const confirm = useConfirm();
+ const alert = useAlert();
+
+ const family = useFamily();
+
+ const [error, setError] = React.useState();
+ const [success, setSuccess] = React.useState();
+
+ const exportData = async () => {
+ loading.show("Export des données");
+ try {
+ setError(undefined);
+ setSuccess(undefined);
+
+ const blob = await DataApi.ExportData(family.familyId);
+ downloadBlob(blob, `Export-${new Date().getTime()}.zip`);
+
+ setSuccess("Export des données effectué avec succès !");
+ } catch (e) {
+ console.error(e);
+ setError("Echec de l'export des données de la famille !");
+ }
+ 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;
+
+ setError(undefined);
+ setSuccess(undefined);
+
+ loading.show(
+ "Restauration des données de généalogie de la famille en cours..."
+ );
+
+ await DataApi.ImportData(family.familyId, file);
+
+ family.reloadFamilyInfo();
+
+ alert(
+ "Import des données de généalogie de la famille effectué avec succès !"
+ );
+ } catch (e) {
+ console.error(e);
+ setError(
+ `Echec de l'import des données de généalogie de la famille ! (${e})`
+ );
+ }
+
+ loading.hide();
+ };
+
+ return (
+
+
+
+ Export / import des données de généalogie
+
+
+ Vous pouvez, à des fins de sauvegardes ou de transfert, exporter et
+ importer l'ensemble des données des membres et des couples de cette
+ famille, sous format ZIP.
+
+
+
+ Attention ! La restauration des données de généalogie de la famille
+ provoque préalablement l'effacement de toutes les données enregistrées
+ dans la famille ! Par ailleurs, la restauration n'est pas réversible !
+
+
+