Compare commits
9 Commits
renovate/l
...
1daca27ba0
| Author | SHA1 | Date | |
|---|---|---|---|
| 1daca27ba0 | |||
| b59db99d02 | |||
| cbdf2b1921 | |||
| 84ef4cac53 | |||
| 299d52f5d7 | |||
| d2a4bfb8e8 | |||
| 88e40fece6 | |||
| c6b518d8de | |||
| cd7462ffb1 |
@@ -36,6 +36,7 @@ import {
|
||||
import { FamilyCouplesListRoute } from "./routes/family/FamilyCouplesListRoute";
|
||||
import { FamilyTreeRoute } from "./routes/family/FamilyTreeRoute";
|
||||
import { FamilyMemberTreeRoute } from "./routes/family/FamilyMemberTreeRoute";
|
||||
import { GenealogyHomeRoute } from "./routes/family/genealogy/GenealogyHomeRoute";
|
||||
|
||||
interface AuthContext {
|
||||
signedIn: boolean;
|
||||
@@ -67,33 +68,44 @@ export function App(): React.ReactElement {
|
||||
<Route path="family/:familyId/*" element={<BaseFamilyRoute />}>
|
||||
<Route path="" element={<FamilyHomeRoute />} />
|
||||
|
||||
<Route path="members" element={<FamilyMembersListRoute />} />
|
||||
<Route
|
||||
path="member/create"
|
||||
element={<FamilyCreateMemberRoute />}
|
||||
/>
|
||||
<Route path="member/:memberId" element={<FamilyMemberRoute />} />
|
||||
<Route
|
||||
path="member/:memberId/edit"
|
||||
element={<FamilyEditMemberRoute />}
|
||||
/>
|
||||
<Route path="genealogy/*">
|
||||
<Route path="" element={<GenealogyHomeRoute />} />
|
||||
|
||||
<Route path="couples" element={<FamilyCouplesListRoute />} />
|
||||
<Route
|
||||
path="couple/create"
|
||||
element={<FamilyCreateCoupleRoute />}
|
||||
/>
|
||||
<Route path="couple/:coupleId" element={<FamilyCoupleRoute />} />
|
||||
<Route
|
||||
path="couple/:coupleId/edit"
|
||||
element={<FamilyEditCoupleRoute />}
|
||||
/>
|
||||
<Route path="members" element={<FamilyMembersListRoute />} />
|
||||
<Route
|
||||
path="member/create"
|
||||
element={<FamilyCreateMemberRoute />}
|
||||
/>
|
||||
<Route
|
||||
path="member/:memberId"
|
||||
element={<FamilyMemberRoute />}
|
||||
/>
|
||||
<Route
|
||||
path="member/:memberId/edit"
|
||||
element={<FamilyEditMemberRoute />}
|
||||
/>
|
||||
|
||||
<Route path="tree" element={<FamilyTreeRoute />} />
|
||||
<Route
|
||||
path="tree/:memberId"
|
||||
element={<FamilyMemberTreeRoute />}
|
||||
/>
|
||||
<Route path="couples" element={<FamilyCouplesListRoute />} />
|
||||
<Route
|
||||
path="couple/create"
|
||||
element={<FamilyCreateCoupleRoute />}
|
||||
/>
|
||||
<Route
|
||||
path="couple/:coupleId"
|
||||
element={<FamilyCoupleRoute />}
|
||||
/>
|
||||
<Route
|
||||
path="couple/:coupleId/edit"
|
||||
element={<FamilyEditCoupleRoute />}
|
||||
/>
|
||||
|
||||
<Route path="tree" element={<FamilyTreeRoute />} />
|
||||
<Route
|
||||
path="tree/:memberId"
|
||||
element={<FamilyMemberTreeRoute />}
|
||||
/>
|
||||
<Route path="*" element={<NotFoundRoute />} />
|
||||
</Route>
|
||||
|
||||
<Route path="settings" element={<FamilySettingsRoute />} />
|
||||
<Route path="users" element={<FamilyUsersListRoute />} />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +234,7 @@ export class FamilyApi {
|
||||
static async UpdateFamily(settings: {
|
||||
id: number;
|
||||
name: string;
|
||||
enable_genealogy: boolean;
|
||||
disable_couple_photos: boolean;
|
||||
}): Promise<void> {
|
||||
await APIClient.exec({
|
||||
@@ -237,6 +242,7 @@ export class FamilyApi {
|
||||
uri: `/family/${settings.id}`,
|
||||
jsonData: {
|
||||
name: settings.name,
|
||||
enable_genealogy: settings.enable_genealogy,
|
||||
disable_couple_photos: settings.disable_couple_photos,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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<Couple> {
|
||||
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<Couple> {
|
||||
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<CouplesList> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await APIClient.exec({
|
||||
uri: `/family/${m.family_id}/couple/${m.id}`,
|
||||
uri: `/family/${m.family_id}/genealogy/couple/${m.id}`,
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
@@ -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<Blob> {
|
||||
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,
|
||||
});
|
||||
@@ -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<Member> {
|
||||
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<Member> {
|
||||
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<MembersList> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
await APIClient.exec({
|
||||
uri: `/family/${m.family_id}/member/${m.id}`,
|
||||
uri: `/family/${m.family_id}/genealogy/member/${m.id}`,
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
@@ -6,8 +6,8 @@ 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 { Couple, CoupleApi } from "../../api/genealogy/CoupleApi";
|
||||
import { Member } from "../../api/genealogy/MemberApi";
|
||||
import { ServerApi } from "../../api/ServerApi";
|
||||
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
|
||||
import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
|
||||
@@ -62,7 +62,7 @@ export function FamilyCreateCoupleRoute(): React.ReactElement {
|
||||
|
||||
const cancel = () => {
|
||||
setShouldQuit(true);
|
||||
n(family.family.URL("couples"));
|
||||
n(family.family.URL("genealogy/couples"));
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -115,7 +115,7 @@ 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();
|
||||
} catch (e) {
|
||||
@@ -486,7 +486,7 @@ export function CouplePage(p: {
|
||||
<div style={{ display: "flex", justifyContent: "end" }}>
|
||||
<RouterLink
|
||||
to={family.family.URL(
|
||||
`member/create?mother=${couple.wife}&father=${couple.husband}`
|
||||
`genealogy/member/create?mother=${couple.wife}&father=${couple.husband}`
|
||||
)}
|
||||
>
|
||||
<Button>Nouveau</Button>
|
||||
|
||||
@@ -6,8 +6,8 @@ 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 { 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";
|
||||
@@ -132,7 +132,13 @@ function CouplesTable(p: {
|
||||
sortable: false,
|
||||
width: 60,
|
||||
renderCell(params) {
|
||||
return <CouplePhoto couple={params.row} />;
|
||||
return (
|
||||
<div
|
||||
style={{ display: "flex", alignItems: "center", height: "100%" }}
|
||||
>
|
||||
<CouplePhoto couple={params.row} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useFamily } from "../../widgets/BaseFamilyRoute";
|
||||
import { FamilyPageTitle } from "../../widgets/FamilyPageTitle";
|
||||
|
||||
export function FamilyHomeRoute(): React.ReactElement {
|
||||
const family = useFamily();
|
||||
return (
|
||||
<>
|
||||
<FamilyPageTitle title="Votre famille" />
|
||||
@@ -12,12 +11,6 @@ export function FamilyHomeRoute(): React.ReactElement {
|
||||
Veuillez utiliser le menu situé à gauche pour accéder aux différentes
|
||||
sections de l'application.
|
||||
</p>
|
||||
<p>Nombre de fiches de membres: {family.members.size}</p>
|
||||
<p>Nombre de fiches de couples: {family.couples.size}</p>
|
||||
<p>
|
||||
Vous pouvez inviter d'autres personnes à rejoindre cette famille en
|
||||
leur donnant une copie du code d'invitation
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -14,8 +14,8 @@ 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 { Couple } from "../../api/genealogy/CoupleApi";
|
||||
import { Member, MemberApi, fmtDate } from "../../api/genealogy/MemberApi";
|
||||
import { ServerApi } from "../../api/ServerApi";
|
||||
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
|
||||
import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
|
||||
@@ -63,7 +63,7 @@ export function FamilyCreateMemberRoute(): React.ReactElement {
|
||||
await family.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 +73,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);
|
||||
@@ -130,7 +130,7 @@ 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();
|
||||
} catch (e) {
|
||||
@@ -155,9 +155,7 @@ export function FamilyMemberRoute(): React.ReactElement {
|
||||
editing={false}
|
||||
onrequestOpenTree={() => 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}
|
||||
/>
|
||||
)}
|
||||
@@ -199,7 +197,7 @@ export function FamilyEditMemberRoute(): React.ReactElement {
|
||||
await family.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 +660,9 @@ export function MemberPage(p: {
|
||||
<div style={{ display: "flex", justifyContent: "end" }}>
|
||||
<RouterLink
|
||||
to={family.family.URL(
|
||||
`couple/create?${member.sex === "F" ? "wife" : "husband"}=${
|
||||
member.id
|
||||
}`
|
||||
`genealogy/couple/create?${
|
||||
member.sex === "F" ? "wife" : "husband"
|
||||
}=${member.id}`
|
||||
)}
|
||||
>
|
||||
<Button>Nouveau</Button>
|
||||
@@ -682,10 +680,7 @@ export function MemberPage(p: {
|
||||
<>Aucun enfant</>
|
||||
) : (
|
||||
p.children.map((c) => (
|
||||
<RouterLink
|
||||
key={c.id}
|
||||
to={family.family.URL(`member/${c.id}`)}
|
||||
>
|
||||
<RouterLink key={c.id} to={family.family.memberURL(c)}>
|
||||
<MemberItem member={c} />
|
||||
</RouterLink>
|
||||
))
|
||||
@@ -694,7 +689,7 @@ export function MemberPage(p: {
|
||||
<div style={{ display: "flex", justifyContent: "end" }}>
|
||||
<RouterLink
|
||||
to={family.family.URL(
|
||||
`member/create?${
|
||||
`genealogy/member/create?${
|
||||
member.sex === "F" ? "mother" : "father"
|
||||
}=${member.id}`
|
||||
)}
|
||||
@@ -714,10 +709,7 @@ export function MemberPage(p: {
|
||||
<>Aucun frère ou sœur</>
|
||||
) : (
|
||||
p.siblings.map((c) => (
|
||||
<RouterLink
|
||||
key={c.id}
|
||||
to={family.family.URL(`member/${c.id}`)}
|
||||
>
|
||||
<RouterLink key={c.id} to={family.family.memberURL(c)}>
|
||||
<MemberItem member={c} />
|
||||
</RouterLink>
|
||||
))
|
||||
@@ -727,7 +719,7 @@ export function MemberPage(p: {
|
||||
<div style={{ display: "flex", justifyContent: "end" }}>
|
||||
<RouterLink
|
||||
to={family.family.URL(
|
||||
`member/create?mother=${member.mother}&father=${member.father}`
|
||||
`genealogy/member/create?mother=${member.mother}&father=${member.father}`
|
||||
)}
|
||||
>
|
||||
<Button>Nouveau</Button>
|
||||
|
||||
@@ -87,7 +87,7 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
|
||||
dense
|
||||
member={member}
|
||||
secondary={
|
||||
<RouterLink to={family.family.URL("tree")}>
|
||||
<RouterLink to={family.family.URL("genealogy/tree")}>
|
||||
<IconButton>
|
||||
<ClearIcon />
|
||||
</IconButton>
|
||||
|
||||
@@ -8,7 +8,12 @@ import { Button, TextField, Tooltip, Typography } from "@mui/material";
|
||||
import { DataGrid, GridActionsCellItem, GridColDef } from "@mui/x-data-grid";
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Member, MemberApi, dateTimestamp, fmtDate } from "../../api/MemberApi";
|
||||
import {
|
||||
Member,
|
||||
MemberApi,
|
||||
dateTimestamp,
|
||||
fmtDate,
|
||||
} from "../../api/genealogy/MemberApi";
|
||||
import { useAlert } from "../../hooks/context_providers/AlertDialogProvider";
|
||||
import { useConfirm } from "../../hooks/context_providers/ConfirmDialogProvider";
|
||||
import { useSnackbar } from "../../hooks/context_providers/SnackbarProvider";
|
||||
@@ -55,7 +60,7 @@ export function FamilyMembersListRoute(): React.ReactElement {
|
||||
}}
|
||||
>
|
||||
<FamilyPageTitle title="Membres de la famille" />
|
||||
<RouterLink to={family.family.URL("member/create")}>
|
||||
<RouterLink to={family.family.URL("genealogy/member/create")}>
|
||||
<Tooltip title="Créer la fiche d'un nouveau membre">
|
||||
<Button startIcon={<AddIcon />}>Nouveau</Button>
|
||||
</Tooltip>
|
||||
@@ -108,7 +113,13 @@ function MembersTable(p: {
|
||||
sortable: false,
|
||||
width: 60,
|
||||
renderCell(params) {
|
||||
return <MemberPhoto member={params.row} />;
|
||||
return (
|
||||
<div
|
||||
style={{ display: "flex", alignItems: "center", height: "100%" }}
|
||||
>
|
||||
<MemberPhoto member={params.row} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ import {
|
||||
} 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 { 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";
|
||||
@@ -55,7 +55,7 @@ export function FamilySettingsRoute(): React.ReactElement {
|
||||
return (
|
||||
<>
|
||||
<FamilySettingsCard />
|
||||
<FamilyExportCard />
|
||||
{family.family.enable_genealogy && <GenealogyExportCard />}
|
||||
<div style={{ textAlign: "center", marginTop: "50px" }}>
|
||||
<Button
|
||||
size="small"
|
||||
@@ -76,6 +76,9 @@ function FamilySettingsCard(): React.ReactElement {
|
||||
const family = useFamily();
|
||||
|
||||
const [newName, setNewName] = React.useState(family.family.name);
|
||||
const [enableGenealogy, setEnableGenealogy] = React.useState(
|
||||
family.family.enable_genealogy
|
||||
);
|
||||
const [disableCouplePhotos, setDisableCouplePhotos] = React.useState(
|
||||
family.family.disable_couple_photos
|
||||
);
|
||||
@@ -93,6 +96,7 @@ function FamilySettingsCard(): React.ReactElement {
|
||||
await FamilyApi.UpdateFamily({
|
||||
id: family.family.family_id,
|
||||
name: newName,
|
||||
enable_genealogy: enableGenealogy,
|
||||
disable_couple_photos: disableCouplePhotos,
|
||||
});
|
||||
|
||||
@@ -144,18 +148,34 @@ function FamilySettingsCard(): React.ReactElement {
|
||||
maxLength: ServerApi.Config.constraints.family_name_len.max,
|
||||
}}
|
||||
/>
|
||||
<Tooltip title="Les photos de couple ne sont pas utilisées en pratique dans les arbres généalogiques. Il est possible de masquer les formulaires d'édition de photos de couple pour limiter le risque de confusion.">
|
||||
<FormControlLabel
|
||||
disabled={!canEdit}
|
||||
control={
|
||||
<Checkbox
|
||||
checked={disableCouplePhotos}
|
||||
onChange={(_e, c) => setDisableCouplePhotos(c)}
|
||||
/>
|
||||
}
|
||||
label="Désactiver les photos de couple"
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<FormControlLabel
|
||||
disabled={!canEdit}
|
||||
control={
|
||||
<Checkbox
|
||||
checked={enableGenealogy}
|
||||
onChange={(_e, c) => setEnableGenealogy(c)}
|
||||
/>
|
||||
}
|
||||
label="Activer la généalogie"
|
||||
/>
|
||||
{enableGenealogy && (
|
||||
<Tooltip
|
||||
title="Les photos de couple ne sont pas utilisées en pratique dans les arbres généalogiques. Il est possible de masquer les formulaires d'édition de photos de couple pour limiter le risque de confusion."
|
||||
arrow
|
||||
>
|
||||
<FormControlLabel
|
||||
disabled={!canEdit}
|
||||
control={
|
||||
<Checkbox
|
||||
checked={disableCouplePhotos}
|
||||
onChange={(_e, c) => setDisableCouplePhotos(c)}
|
||||
/>
|
||||
}
|
||||
label="Désactiver les photos de couple"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
@@ -171,7 +191,7 @@ function FamilySettingsCard(): React.ReactElement {
|
||||
);
|
||||
}
|
||||
|
||||
function FamilyExportCard(): React.ReactElement {
|
||||
function GenealogyExportCard(): React.ReactElement {
|
||||
const loading = useLoadingMessage();
|
||||
const confirm = useConfirm();
|
||||
const alert = useAlert();
|
||||
@@ -234,7 +254,7 @@ function FamilyExportCard(): React.ReactElement {
|
||||
<FamilyCard error={error} success={success}>
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant="h5" component="div">
|
||||
Export / import des données de la famille
|
||||
Export / import des données de généalogie
|
||||
</Typography>
|
||||
<p>
|
||||
Vous pouvez, à des fins de sauvegardes ou de transfert, exporter et
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { useFamily } from "../../../widgets/BaseFamilyRoute";
|
||||
import { FamilyPageTitle } from "../../../widgets/FamilyPageTitle";
|
||||
|
||||
export function GenealogyHomeRoute(): React.ReactElement {
|
||||
const family = useFamily();
|
||||
return (
|
||||
<>
|
||||
<FamilyPageTitle title="Généalogie de votre famille" />
|
||||
<div style={{ margin: "20px" }}>
|
||||
<p>
|
||||
Depuis cette section de l'application, vous pouvez afficher et
|
||||
compléter l'abre généalogique de votre famille.
|
||||
</p>
|
||||
<p> </p>
|
||||
<p>Nombre de fiches de membres: {family.members.size}</p>
|
||||
<p>Nombre de fiches de couples: {family.couples.size}</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Couple, CouplesList } from "../api/CoupleApi";
|
||||
import { Member, MembersList, dateTimestamp } from "../api/MemberApi";
|
||||
import { Couple, CouplesList } from "../api/genealogy/CoupleApi";
|
||||
import { Member, MembersList, dateTimestamp } from "../api/genealogy/MemberApi";
|
||||
|
||||
export interface CoupleInformation {
|
||||
couple: Couple;
|
||||
|
||||
@@ -26,9 +26,9 @@ import {
|
||||
} from "@mui/material";
|
||||
import React from "react";
|
||||
import { Outlet, useLocation, useParams } from "react-router-dom";
|
||||
import { CoupleApi, CouplesList } from "../api/CoupleApi";
|
||||
import { ExtendedFamilyInfo, FamilyApi } from "../api/FamilyApi";
|
||||
import { MemberApi, MembersList } from "../api/MemberApi";
|
||||
import { CoupleApi, CouplesList } from "../api/genealogy/CoupleApi";
|
||||
import { MemberApi, MembersList } from "../api/genealogy/MemberApi";
|
||||
import { useAlert } from "../hooks/context_providers/AlertDialogProvider";
|
||||
import { useConfirm } from "../hooks/context_providers/ConfirmDialogProvider";
|
||||
import { useSnackbar } from "../hooks/context_providers/SnackbarProvider";
|
||||
@@ -147,13 +147,21 @@ export function BaseFamilyRoute(): React.ReactElement {
|
||||
|
||||
<FamilyLink icon={<HomeIcon />} label="Accueil" uri="" />
|
||||
|
||||
<Divider sx={{ my: 1 }} />
|
||||
<ListSubheader component="div">Généalogie</ListSubheader>
|
||||
|
||||
<FamilyLink
|
||||
icon={<HomeIcon />}
|
||||
label="Accueil"
|
||||
uri="genealogy"
|
||||
/>
|
||||
<FamilyLink
|
||||
icon={<Icon path={mdiCrowd} size={1} />}
|
||||
label="Membres"
|
||||
uri="members"
|
||||
uri="genealogy/members"
|
||||
secondaryAction={
|
||||
<Tooltip title="Créer une nouvelle fiche de membre">
|
||||
<RouterLink to={family!.URL("member/create")}>
|
||||
<RouterLink to={family!.URL("genealogy/member/create")}>
|
||||
<IconButton>
|
||||
<Icon path={mdiPlus} size={0.75} />
|
||||
</IconButton>
|
||||
@@ -165,10 +173,10 @@ export function BaseFamilyRoute(): React.ReactElement {
|
||||
<FamilyLink
|
||||
icon={<Icon path={mdiHumanMaleFemale} size={1} />}
|
||||
label="Couples"
|
||||
uri="couples"
|
||||
uri="genealogy/couples"
|
||||
secondaryAction={
|
||||
<Tooltip title="Créer une nouvelle fiche de couple">
|
||||
<RouterLink to={family!.URL("couple/create")}>
|
||||
<RouterLink to={family!.URL("genealogy/couple/create")}>
|
||||
<IconButton>
|
||||
<Icon path={mdiPlus} size={0.75} />
|
||||
</IconButton>
|
||||
@@ -180,7 +188,7 @@ export function BaseFamilyRoute(): React.ReactElement {
|
||||
<FamilyLink
|
||||
icon={<Icon path={mdiFamilyTree} size={1} />}
|
||||
label="Arbre"
|
||||
uri="tree"
|
||||
uri="genealogy/tree"
|
||||
/>
|
||||
|
||||
<Divider sx={{ my: 1 }} />
|
||||
|
||||
@@ -5,8 +5,8 @@ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||
import { TreeItem, SimpleTreeView } from "@mui/x-tree-view";
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Couple } from "../api/CoupleApi";
|
||||
import { Member, fmtDate } from "../api/MemberApi";
|
||||
import { Couple } from "../api/genealogy/CoupleApi";
|
||||
import { Member, fmtDate } from "../api/genealogy/MemberApi";
|
||||
import { FamilyTreeNode } from "../utils/family_tree";
|
||||
import { useFamily } from "./BaseFamilyRoute";
|
||||
import { MemberPhoto } from "./MemberPhoto";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Avatar } from "@mui/material";
|
||||
import { Couple } from "../api/CoupleApi";
|
||||
import { Couple } from "../api/genealogy/CoupleApi";
|
||||
|
||||
export function CouplePhoto(p: {
|
||||
couple: Couple;
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
ListItemSecondaryAction,
|
||||
ListItemText,
|
||||
} from "@mui/material";
|
||||
import { Member, fmtDate } from "../api/MemberApi";
|
||||
import { Member, fmtDate } from "../api/genealogy/MemberApi";
|
||||
import { MemberPhoto } from "./MemberPhoto";
|
||||
import Icon from "@mdi/react";
|
||||
import FemaleIcon from "@mui/icons-material/Female";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Avatar } from "@mui/material";
|
||||
import { Member } from "../api/MemberApi";
|
||||
import { Member } from "../api/genealogy/MemberApi";
|
||||
|
||||
export function MemberPhoto(p: {
|
||||
member?: Member;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Stack, TextField, Typography } from "@mui/material";
|
||||
import { NumberConstraint, ServerApi } from "../../api/ServerApi";
|
||||
import { DateValue, fmtDate } from "../../api/MemberApi";
|
||||
import { DateValue, fmtDate } from "../../api/genealogy/MemberApi";
|
||||
import { PropEdit } from "./PropEdit";
|
||||
|
||||
export function DateInput(p: {
|
||||
|
||||
@@ -2,7 +2,7 @@ import ClearIcon from "@mui/icons-material/Clear";
|
||||
import { Autocomplete, IconButton, TextField, Typography } from "@mui/material";
|
||||
import React from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { Member } from "../../api/MemberApi";
|
||||
import { Member } from "../../api/genealogy/MemberApi";
|
||||
import { useFamily } from "../BaseFamilyRoute";
|
||||
import { MemberItem } from "../MemberItem";
|
||||
|
||||
@@ -30,7 +30,7 @@ export function MemberInput(p: {
|
||||
onClick={
|
||||
!p.editable
|
||||
? () => {
|
||||
n(family.family.URL(`member/${member.id}`));
|
||||
n(family.family.memberURL(member));
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
Radio,
|
||||
Typography,
|
||||
} from "@mui/material";
|
||||
import { Sex } from "../../api/MemberApi";
|
||||
import { Sex } from "../../api/genealogy/MemberApi";
|
||||
|
||||
export function SexSelection(p: {
|
||||
readonly?: boolean;
|
||||
|
||||
@@ -5,8 +5,8 @@ import { IconButton, Tooltip } from "@mui/material";
|
||||
import jsPDF from "jspdf";
|
||||
import React from "react";
|
||||
import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
|
||||
import { Couple } from "../../api/CoupleApi";
|
||||
import { Member } from "../../api/MemberApi";
|
||||
import { Couple } from "../../api/genealogy/CoupleApi";
|
||||
import { Member } from "../../api/genealogy/MemberApi";
|
||||
import { useDarkTheme } from "../../hooks/context_providers/DarkThemeProvider";
|
||||
import { FamilyTreeNode } from "../../utils/family_tree";
|
||||
import { downloadBlob } from "../../utils/files_utils";
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-- Remove column to toggle genealogy
|
||||
ALTER TABLE public.families
|
||||
DROP COLUMN enable_genealogy;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- Add column to toggle genealogy
|
||||
ALTER TABLE public.families
|
||||
ADD enable_genealogy boolean NOT NULL DEFAULT false;
|
||||
COMMENT
|
||||
ON COLUMN public.families.enable_genealogy IS 'Specify whether genealogy feature is enabled for the family';
|
||||
@@ -79,6 +79,7 @@ pub async fn list(token: LoginToken) -> HttpResult {
|
||||
struct RichFamilyInfo {
|
||||
#[serde(flatten)]
|
||||
membership: FamilyMembership,
|
||||
enable_genealogy: bool,
|
||||
disable_couple_photos: bool,
|
||||
}
|
||||
|
||||
@@ -88,6 +89,7 @@ pub async fn single_info(f: FamilyInPath) -> HttpResult {
|
||||
let family = families_service::get_by_id(f.family_id()).await?;
|
||||
Ok(HttpResponse::Ok().json(RichFamilyInfo {
|
||||
membership,
|
||||
enable_genealogy: family.enable_genealogy,
|
||||
disable_couple_photos: family.disable_couple_photos,
|
||||
}))
|
||||
}
|
||||
@@ -102,6 +104,7 @@ pub async fn leave(f: FamilyInPath) -> HttpResult {
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct UpdateFamilyBody {
|
||||
name: String,
|
||||
enable_genealogy: bool,
|
||||
disable_couple_photos: bool,
|
||||
}
|
||||
|
||||
@@ -119,6 +122,7 @@ pub async fn update(
|
||||
|
||||
let mut family = families_service::get_by_id(f.family_id()).await?;
|
||||
family.name = req.0.name;
|
||||
family.enable_genealogy = req.0.enable_genealogy;
|
||||
family.disable_couple_photos = req.0.disable_couple_photos;
|
||||
families_service::update_family(&family).await?;
|
||||
|
||||
|
||||
@@ -137,71 +137,71 @@ async fn main() -> std::io::Result<()> {
|
||||
"/family/{id}/user/{user_id}",
|
||||
web::delete().to(families_controller::delete_membership),
|
||||
)
|
||||
// Members controller
|
||||
// [GENEALOGY] Members controller
|
||||
.route(
|
||||
"/family/{id}/member/create",
|
||||
"/family/{id}/genealogy/member/create",
|
||||
web::post().to(members_controller::create),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/members",
|
||||
"/family/{id}/genealogy/members",
|
||||
web::get().to(members_controller::get_all),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/member/{member_id}",
|
||||
"/family/{id}/genealogy/member/{member_id}",
|
||||
web::get().to(members_controller::get_single),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/member/{member_id}",
|
||||
"/family/{id}/genealogy/member/{member_id}",
|
||||
web::put().to(members_controller::update),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/member/{member_id}",
|
||||
"/family/{id}/genealogy/member/{member_id}",
|
||||
web::delete().to(members_controller::delete),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/member/{member_id}/photo",
|
||||
"/family/{id}/genealogy/member/{member_id}/photo",
|
||||
web::put().to(members_controller::set_photo),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/member/{member_id}/photo",
|
||||
"/family/{id}/genealogy/member/{member_id}/photo",
|
||||
web::delete().to(members_controller::remove_photo),
|
||||
)
|
||||
// Couples controller
|
||||
// [GENEALOGY] Couples controller
|
||||
.route(
|
||||
"/family/{id}/couple/create",
|
||||
"/family/{id}/genealogy/couple/create",
|
||||
web::post().to(couples_controller::create),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/couples",
|
||||
"/family/{id}/genealogy/couples",
|
||||
web::get().to(couples_controller::get_all),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/couple/{couple_id}",
|
||||
"/family/{id}/genealogy/couple/{couple_id}",
|
||||
web::get().to(couples_controller::get_single),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/couple/{couple_id}",
|
||||
"/family/{id}/genealogy/couple/{couple_id}",
|
||||
web::put().to(couples_controller::update),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/couple/{couple_id}",
|
||||
"/family/{id}/genealogy/couple/{couple_id}",
|
||||
web::delete().to(couples_controller::delete),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/couple/{couple_id}/photo",
|
||||
"/family/{id}/genealogy/couple/{couple_id}/photo",
|
||||
web::put().to(couples_controller::set_photo),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/couple/{couple_id}/photo",
|
||||
"/family/{id}/genealogy/couple/{couple_id}/photo",
|
||||
web::delete().to(couples_controller::remove_photo),
|
||||
)
|
||||
// Data controller
|
||||
// [GENEALOGY] Data controller
|
||||
.route(
|
||||
"/family/{id}/data/export",
|
||||
"/family/{id}/genealogy/data/export",
|
||||
web::get().to(data_controller::export_family),
|
||||
)
|
||||
.route(
|
||||
"/family/{id}/data/import",
|
||||
"/family/{id}/genealogy/data/import",
|
||||
web::put().to(data_controller::import_family),
|
||||
)
|
||||
// Photos controller
|
||||
|
||||
@@ -65,6 +65,7 @@ pub struct Family {
|
||||
pub name: String,
|
||||
pub invitation_code: String,
|
||||
pub disable_couple_photos: bool,
|
||||
pub enable_genealogy: bool,
|
||||
}
|
||||
|
||||
impl Family {
|
||||
|
||||
@@ -29,6 +29,7 @@ diesel::table! {
|
||||
#[max_length = 7]
|
||||
invitation_code -> Varchar,
|
||||
disable_couple_photos -> Bool,
|
||||
enable_genealogy -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ pub async fn update_family(family: &Family) -> anyhow::Result<()> {
|
||||
.set((
|
||||
families::dsl::name.eq(family.name.clone()),
|
||||
families::dsl::invitation_code.eq(family.invitation_code.clone()),
|
||||
families::dsl::enable_genealogy.eq(family.enable_genealogy),
|
||||
families::dsl::disable_couple_photos.eq(family.disable_couple_photos),
|
||||
))
|
||||
.execute(conn)
|
||||
|
||||
Reference in New Issue
Block a user