Compare commits
69 Commits
e79a474e91
...
f16bee5927
Author | SHA1 | Date | |
---|---|---|---|
f16bee5927 | |||
61082619fe | |||
79126ebbd7 | |||
d87f04b76b | |||
da476ea4d7 | |||
0266c0c8b3 | |||
5863c346d0 | |||
b0087f2dfb | |||
97340b4ae8 | |||
7dcc5e1d29 | |||
240d830526 | |||
c1e7ed9034 | |||
8324f9c501 | |||
d715a61255 | |||
eaee7f601e | |||
042776d491 | |||
900f6b8af8 | |||
86ada0da5d | |||
3d59629f7a | |||
dffc00382a | |||
2dd9fc3469 | |||
cfd0cb37f5 | |||
d14557ac49 | |||
e188949ac0 | |||
d2bddb62bc | |||
b382f6bb85 | |||
16ccb39b36 | |||
60cc8a2401 | |||
11715864a4 | |||
a79febee92 | |||
554188e511 | |||
a628bdee49 | |||
873a2aae95 | |||
1fe01ff893 | |||
ceb0f11128 | |||
bf3562c9e0 | |||
1251d61352 | |||
f2a58b5dcd | |||
d74f4aee61 | |||
0788b6462c | |||
26ee9e5d72 | |||
4c12a3c56b | |||
de767c90b9 | |||
be8066de17 | |||
7262c66c90 | |||
73ff552104 | |||
98920a485f | |||
4a2d95f64f | |||
c55b6f724e | |||
2ad40c4ba0 | |||
5cb64ad015 | |||
a60179eb0e | |||
2956777cf5 | |||
65842c7f30 | |||
b7b759f5e9 | |||
10594732ba | |||
36ee8bf5b7 | |||
3dbbe50b83 | |||
35d7bd493e | |||
e4ae43f182 | |||
cef5d3c416 | |||
34efa48c3e | |||
cba850251f | |||
9f25f88695 | |||
2043afadbb | |||
74503d1eaa | |||
c3a173128e | |||
6005955884 | |||
3aef093883 |
@ -19,7 +19,7 @@ docker-compose up
|
|||||||
3. Install Diesel CLI:
|
3. Install Diesel CLI:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install libpq5 libpq-dev
|
sudo apt install libpq5 libpq-dev pkg-config libssl-dev
|
||||||
cargo install diesel_cli --no-default-features --features postgres
|
cargo install diesel_cli --no-default-features --features postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -51,4 +51,4 @@ evan@qlik.example
|
|||||||
franklin@qlik.example
|
franklin@qlik.example
|
||||||
```
|
```
|
||||||
|
|
||||||
Password: `Password1!`
|
Password: `Password1!`
|
||||||
|
@ -1 +1 @@
|
|||||||
REACT_APP_BACKEND=http://localhost:8000
|
VITE_APP_BACKEND=http://localhost:8000
|
@ -1 +1 @@
|
|||||||
REACT_APP_BACKEND=https://geneit-backend.communiquons.org
|
VITE_APP_BACKEND=https://geneit-backend.communiquons.org
|
||||||
|
1
geneit_app/.gitignore
vendored
1
geneit_app/.gitignore
vendored
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
|
/dist
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
@ -2,25 +2,25 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="Web site created using create-react-app"
|
content="Web site created using create-react-app"
|
||||||
/>
|
/>
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="/logo192.png" />
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
-->
|
-->
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
<link rel="manifest" href="/manifest.json" />
|
||||||
<!--
|
<!--
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
Notice the use of in the tags above.
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
Unlike "/favicon.ico" or "favicon.ico", "/favicon.ico" will
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
-->
|
-->
|
||||||
@ -39,5 +39,7 @@
|
|||||||
To begin the development, run `npm start` or `yarn start`.
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
17377
geneit_app/package-lock.json
generated
17377
geneit_app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
|||||||
"name": "geneit_app",
|
"name": "geneit_app",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@emotion/react": "^11.11.0",
|
"@emotion/react": "^11.11.0",
|
||||||
@ -18,29 +19,28 @@
|
|||||||
"@testing-library/react": "^13.4.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
"@types/jest": "^27.5.2",
|
"@types/jest": "^27.5.2",
|
||||||
"@types/node": "^16.18.34",
|
|
||||||
"@types/react": "^18.2.8",
|
"@types/react": "^18.2.8",
|
||||||
"@types/react-dom": "^18.2.4",
|
"@types/react-dom": "^18.2.4",
|
||||||
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"date-and-time": "^3.0.1",
|
"date-and-time": "^3.0.1",
|
||||||
"email-validator": "^2.0.4",
|
"email-validator": "^2.0.4",
|
||||||
"family-chart": "^0.0.0-beta-1",
|
|
||||||
"filesize": "^10.0.9",
|
"filesize": "^10.0.9",
|
||||||
"jspdf": "^2.5.1",
|
"jspdf": "^2.5.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-easy-crop": "^5.0.0",
|
"react-easy-crop": "^5.0.0",
|
||||||
"react-router-dom": "^6.11.2",
|
"react-router-dom": "^6.11.2",
|
||||||
"react-scripts": "^5.0.1",
|
|
||||||
"react-zoom-pan-pinch": "^3.1.0",
|
"react-zoom-pan-pinch": "^3.1.0",
|
||||||
"svg2pdf.js": "^2.2.2",
|
"svg2pdf.js": "^2.2.3",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
|
"vite": "^5.0.8",
|
||||||
|
"vite-tsconfig-paths": "^4.2.2",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "vite",
|
||||||
"build": "react-scripts build",
|
"build": "tsc && vite build",
|
||||||
"test": "react-scripts test",
|
"preview": "vite preview"
|
||||||
"eject": "react-scripts eject"
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": [
|
"extends": [
|
||||||
|
@ -16,7 +16,7 @@ export class APIClient {
|
|||||||
* Get backend URL
|
* Get backend URL
|
||||||
*/
|
*/
|
||||||
static backendURL(): string {
|
static backendURL(): string {
|
||||||
const URL = process.env.REACT_APP_BACKEND ?? "";
|
const URL = import.meta.env.VITE_APP_BACKEND ?? "";
|
||||||
if (URL.length === 0) throw new Error("Backend URL undefined!");
|
if (URL.length === 0) throw new Error("Backend URL undefined!");
|
||||||
return URL;
|
return URL;
|
||||||
}
|
}
|
||||||
|
1
geneit_app/src/react-app-env.d.ts
vendored
1
geneit_app/src/react-app-env.d.ts
vendored
@ -1 +0,0 @@
|
|||||||
/// <reference types="react-scripts" />
|
|
@ -26,13 +26,11 @@ import { useFamily } from "../../widgets/BaseFamilyRoute";
|
|||||||
import { BasicFamilyTree } from "../../widgets/BasicFamilyTree";
|
import { BasicFamilyTree } from "../../widgets/BasicFamilyTree";
|
||||||
import { MemberItem } from "../../widgets/MemberItem";
|
import { MemberItem } from "../../widgets/MemberItem";
|
||||||
import { RouterLink } from "../../widgets/RouterLink";
|
import { RouterLink } from "../../widgets/RouterLink";
|
||||||
import { ComplexFamilyTree } from "../../widgets/complex_family_tree/ComplexFamilyTree";
|
|
||||||
import { SimpleFamilyTree } from "../../widgets/simple_family_tree/SimpleFamilyTree";
|
import { SimpleFamilyTree } from "../../widgets/simple_family_tree/SimpleFamilyTree";
|
||||||
|
|
||||||
enum CurrTab {
|
enum CurrTab {
|
||||||
BasicTree,
|
BasicTree,
|
||||||
SimpleTree,
|
SimpleTree,
|
||||||
AdvancedTree,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum TreeMode {
|
enum TreeMode {
|
||||||
@ -149,7 +147,6 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
|
|||||||
>
|
>
|
||||||
<Tab tabIndex={CurrTab.BasicTree} label="Basique" />
|
<Tab tabIndex={CurrTab.BasicTree} label="Basique" />
|
||||||
<Tab tabIndex={CurrTab.SimpleTree} label="Simple" />
|
<Tab tabIndex={CurrTab.SimpleTree} label="Simple" />
|
||||||
<Tab tabIndex={CurrTab.AdvancedTree} label="Avancé" />
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -157,14 +154,8 @@ export function FamilyMemberTreeRoute(): React.ReactElement {
|
|||||||
<Paper style={{ flex: "1", display: "flex", flexDirection: "column" }}>
|
<Paper style={{ flex: "1", display: "flex", flexDirection: "column" }}>
|
||||||
{currTab === CurrTab.BasicTree ? (
|
{currTab === CurrTab.BasicTree ? (
|
||||||
<BasicFamilyTree tree={tree!} depth={currDepth} />
|
<BasicFamilyTree tree={tree!} depth={currDepth} />
|
||||||
) : currTab === CurrTab.SimpleTree ? (
|
|
||||||
<SimpleFamilyTree tree={tree!} depth={currDepth} />
|
|
||||||
) : (
|
) : (
|
||||||
<ComplexFamilyTree
|
<SimpleFamilyTree tree={tree!} depth={currDepth} />
|
||||||
tree={tree!}
|
|
||||||
isUp={currMode === TreeMode.Ascending}
|
|
||||||
depth={currDepth}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</Paper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
export function isDebug(): boolean {
|
export function isDebug(): boolean {
|
||||||
return !process.env.NODE_ENV || process.env.NODE_ENV === "development";
|
return (
|
||||||
|
!import.meta.env.NODE_ENV || import.meta.env.NODE_ENV === "development"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import { unstable_useBlocker as useBlocker } from "react-router-dom";
|
import { useBlocker } from "react-router-dom";
|
||||||
|
|
||||||
export function ConfirmLeaveWithoutSaveDialog(p: {
|
export function ConfirmLeaveWithoutSaveDialog(p: {
|
||||||
shouldBlock: boolean;
|
shouldBlock: boolean;
|
||||||
|
@ -1,339 +0,0 @@
|
|||||||
import { mdiXml } from "@mdi/js";
|
|
||||||
import Icon from "@mdi/react";
|
|
||||||
import PictureAsPdfIcon from "@mui/icons-material/PictureAsPdf";
|
|
||||||
import { IconButton, Tooltip } from "@mui/material";
|
|
||||||
import f3, { f3Data } from "family-chart";
|
|
||||||
import { jsPDF } from "jspdf";
|
|
||||||
import React from "react";
|
|
||||||
import "svg2pdf.js";
|
|
||||||
import { Couple } from "../../api/CoupleApi";
|
|
||||||
import { Member, fmtDate } from "../../api/MemberApi";
|
|
||||||
import { useDarkTheme } from "../../hooks/context_providers/DarkThemeProvider";
|
|
||||||
import {
|
|
||||||
FamilyTreeNode,
|
|
||||||
getAvailableMembers,
|
|
||||||
treeHeight,
|
|
||||||
treeWidth,
|
|
||||||
} from "../../utils/family_tree";
|
|
||||||
import { downloadBlob } from "../../utils/files_utils";
|
|
||||||
import "./family-chart.css";
|
|
||||||
|
|
||||||
export function ComplexFamilyTree(p: {
|
|
||||||
tree: FamilyTreeNode;
|
|
||||||
isUp: boolean;
|
|
||||||
depth: number;
|
|
||||||
}): React.ReactElement {
|
|
||||||
const darkTheme = useDarkTheme();
|
|
||||||
|
|
||||||
const applyTree = (container: HTMLDivElement) => {
|
|
||||||
if (!container) return;
|
|
||||||
|
|
||||||
const store = f3.createStore({
|
|
||||||
data: treeToF3Data(p.tree, p.isUp, p.depth),
|
|
||||||
node_separation: 250,
|
|
||||||
level_separation: 150,
|
|
||||||
});
|
|
||||||
const view = f3.d3AnimationView({
|
|
||||||
store,
|
|
||||||
cont: container,
|
|
||||||
});
|
|
||||||
const Card = f3.elements.Card({
|
|
||||||
store,
|
|
||||||
svg: view.svg,
|
|
||||||
card_dim: {
|
|
||||||
w: 210,
|
|
||||||
h: 120,
|
|
||||||
text_x: 5,
|
|
||||||
text_y: 75,
|
|
||||||
img_w: 60,
|
|
||||||
img_h: 70,
|
|
||||||
img_x: 5,
|
|
||||||
img_y: 5,
|
|
||||||
},
|
|
||||||
card_display: [
|
|
||||||
(d) =>
|
|
||||||
`${d.data.first_name || ""} ${d.data.last_name || ""} ${
|
|
||||||
d.data.dead ? "✝" : ""
|
|
||||||
}`,
|
|
||||||
(d) => {
|
|
||||||
let birthDeath = [];
|
|
||||||
if (d.data.birthday) birthDeath.push(d.data.birthday);
|
|
||||||
if (d.data.deathday) birthDeath.push(d.data.deathday);
|
|
||||||
|
|
||||||
let s = birthDeath.join(" -> ");
|
|
||||||
|
|
||||||
if (d.data.wedding_state || d.data.dateOfWedding) {
|
|
||||||
let weddingInfo = [];
|
|
||||||
if (d.data.wedding_state) weddingInfo.push(d.data.wedding_state);
|
|
||||||
if (d.data.dateOfWedding)
|
|
||||||
weddingInfo.push("Mariage : " + d.data.dateOfWedding);
|
|
||||||
s += `</tspan> <tspan x="0" dy="14" font-size="10">${weddingInfo.join(
|
|
||||||
" - "
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
},
|
|
||||||
],
|
|
||||||
mini_tree: true,
|
|
||||||
link_break: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Patch generated card
|
|
||||||
const PatchedCard: f3.F3CardBuilder = (p) => {
|
|
||||||
const res = Card(p);
|
|
||||||
|
|
||||||
// Patch card colors for PDF export
|
|
||||||
res
|
|
||||||
.querySelector(".card-male")
|
|
||||||
?.querySelector(".card-body-rect")
|
|
||||||
?.setAttribute("fill", "#add8e6");
|
|
||||||
res
|
|
||||||
.querySelector(".card-female")
|
|
||||||
?.querySelector(".card-body-rect")
|
|
||||||
?.setAttribute("fill", "#ffb6c1");
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
view.setCard(PatchedCard);
|
|
||||||
store.setOnUpdate((props) => view.update(props || {}));
|
|
||||||
store.update.tree({ initial: false, transition_time: 0 });
|
|
||||||
};
|
|
||||||
|
|
||||||
const doExport = async (onlySVG: boolean) => {
|
|
||||||
const docWidth = treeWidth(p.tree) * 65;
|
|
||||||
const docHeight = treeHeight(p.tree) * 60;
|
|
||||||
console.info(`Tree w=${treeWidth(p.tree)} h=${treeHeight(p.tree)}`);
|
|
||||||
|
|
||||||
// Clone the SVG to manipulate it
|
|
||||||
const container = document.createElement("div");
|
|
||||||
container.classList.add("f3", "f3-export");
|
|
||||||
container.style.width = docWidth + "px";
|
|
||||||
container.style.height = docHeight + "px";
|
|
||||||
document.body.appendChild(container);
|
|
||||||
applyTree(container);
|
|
||||||
|
|
||||||
const target = container.children[0];
|
|
||||||
|
|
||||||
await new Promise((res) => setTimeout(() => res(null), 100));
|
|
||||||
|
|
||||||
// SVG manipulations (adaptations to export)
|
|
||||||
let dstSVG = target.innerHTML.replaceAll(
|
|
||||||
`<path class="link" fill="none" stroke="#fff"`,
|
|
||||||
`<path class="link" fill="none" stroke="#000"`
|
|
||||||
);
|
|
||||||
|
|
||||||
dstSVG = dstSVG.replaceAll(
|
|
||||||
`class="text-overflow-mask"`,
|
|
||||||
`class="text-overflow-mask" fill="transparent"`
|
|
||||||
);
|
|
||||||
|
|
||||||
dstSVG = dstSVG.replaceAll(`>UNKNOWN<`, `fill="#000">INCONNU<`);
|
|
||||||
|
|
||||||
dstSVG = dstSVG.replaceAll(
|
|
||||||
`class="card-outline`,
|
|
||||||
`fill="transparent" class="card-outline`
|
|
||||||
);
|
|
||||||
|
|
||||||
dstSVG = dstSVG.replaceAll("✝", " ");
|
|
||||||
|
|
||||||
// Download in SVG format
|
|
||||||
if (onlySVG) {
|
|
||||||
// Fix background color (first rect background)
|
|
||||||
dstSVG = dstSVG.replace(
|
|
||||||
`fill="transparent"></rect>`,
|
|
||||||
`fill="white"></rect>`
|
|
||||||
);
|
|
||||||
|
|
||||||
const blob = new Blob([`<svg>${dstSVG}</svg>`], {
|
|
||||||
type: "image/svg+xml",
|
|
||||||
});
|
|
||||||
|
|
||||||
downloadBlob(blob, "ArbreGenealogique.svg");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download in PDF format
|
|
||||||
//navigator.clipboard.writeText(dstSVG);
|
|
||||||
target.innerHTML = dstSVG;
|
|
||||||
|
|
||||||
const doc = new jsPDF({
|
|
||||||
orientation: "l",
|
|
||||||
format: [docHeight, docWidth],
|
|
||||||
});
|
|
||||||
|
|
||||||
await doc.svg(target, {
|
|
||||||
height: docHeight,
|
|
||||||
width: docWidth,
|
|
||||||
});
|
|
||||||
|
|
||||||
container.remove();
|
|
||||||
|
|
||||||
// Save the created pdf
|
|
||||||
doc.save("ArbreGenealogique.pdf");
|
|
||||||
};
|
|
||||||
|
|
||||||
const exportPDF = () => doExport(false);
|
|
||||||
|
|
||||||
const exportSVG = () => doExport(true);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div style={{ textAlign: "right" }}>
|
|
||||||
<Tooltip title="Exporter le graphique au format PDF">
|
|
||||||
<IconButton onClick={exportPDF}>
|
|
||||||
<PictureAsPdfIcon />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip title="Exporter le graphique au format SVG">
|
|
||||||
<IconButton onClick={exportSVG}>
|
|
||||||
<Icon path={mdiXml} size={1} />
|
|
||||||
</IconButton>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{ width: "100%" }}
|
|
||||||
className={`f3 ${darkTheme.enabled ? "f3-dark" : "f3-light"}`}
|
|
||||||
id="FamilyChart"
|
|
||||||
ref={applyTree}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function treeToF3Data(
|
|
||||||
node: FamilyTreeNode,
|
|
||||||
isUp: boolean,
|
|
||||||
depth: number
|
|
||||||
): f3Data[] {
|
|
||||||
const availableMembers = getAvailableMembers(node, depth);
|
|
||||||
|
|
||||||
const list: f3Data[] = [];
|
|
||||||
if (isUp) treeToF3DataUpRecurse(node, list, availableMembers);
|
|
||||||
else treeToF3DataDownRecurse(node, list, availableMembers);
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
function memberData(m: Member, c?: Couple): f3.f3DataData {
|
|
||||||
return {
|
|
||||||
first_name: m.first_name ?? "_",
|
|
||||||
last_name: m.last_name ?? "_",
|
|
||||||
gender: m.sex ?? "M",
|
|
||||||
avatar: m.thumbnailURL ?? undefined,
|
|
||||||
dead: m.dead,
|
|
||||||
birthday: m.dateOfBirth ? fmtDate(m.dateOfBirth) : undefined,
|
|
||||||
deathday: m.dateOfDeath ? fmtDate(m.dateOfDeath) : undefined,
|
|
||||||
wedding_state: c?.stateFr,
|
|
||||||
dateOfWedding: c?.dateOfWedding ? fmtDate(c?.dateOfWedding) : undefined,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function treeToF3DataUpRecurse(
|
|
||||||
node: FamilyTreeNode,
|
|
||||||
array: f3Data[],
|
|
||||||
availableMembers: Set<number>,
|
|
||||||
child?: number,
|
|
||||||
spouses?: number[]
|
|
||||||
) {
|
|
||||||
if (!availableMembers.has(node.member.id)) return;
|
|
||||||
|
|
||||||
array.push({
|
|
||||||
data: memberData(node.member),
|
|
||||||
id: node.member.id.toString(),
|
|
||||||
rels: {
|
|
||||||
father:
|
|
||||||
node.member.father && availableMembers.has(node.member.father)
|
|
||||||
? node.member.father.toString()
|
|
||||||
: undefined,
|
|
||||||
mother:
|
|
||||||
node.member.mother && availableMembers.has(node.member.mother)
|
|
||||||
? node.member.mother.toString()
|
|
||||||
: undefined,
|
|
||||||
|
|
||||||
spouses: spouses
|
|
||||||
?.filter((c) => c !== node.member.id)
|
|
||||||
.map((c) => c.toString()),
|
|
||||||
children: child ? [child.toString()] : undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const parentSpouses = node.down?.map((c) => c.member.id);
|
|
||||||
|
|
||||||
node.down?.forEach((d) =>
|
|
||||||
treeToF3DataUpRecurse(
|
|
||||||
d,
|
|
||||||
array,
|
|
||||||
availableMembers,
|
|
||||||
node.member.id,
|
|
||||||
parentSpouses
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function treeToF3DataDownRecurse(
|
|
||||||
node: FamilyTreeNode,
|
|
||||||
array: f3Data[],
|
|
||||||
availableMembers: Set<number>
|
|
||||||
) {
|
|
||||||
if (!availableMembers.has(node.member.id)) return;
|
|
||||||
|
|
||||||
// Get all members ids
|
|
||||||
let children = node?.down?.map((c) => c.member.id) ?? [];
|
|
||||||
node.couples?.map((c) => c.down.forEach((m) => children.push(m.member.id)));
|
|
||||||
|
|
||||||
children = children.filter((c) => availableMembers.has(c));
|
|
||||||
|
|
||||||
array.push({
|
|
||||||
data: memberData(node.member),
|
|
||||||
id: node.member.id.toString(),
|
|
||||||
rels: {
|
|
||||||
father:
|
|
||||||
node.member.father && availableMembers.has(node.member.father)
|
|
||||||
? node.member.father.toString()
|
|
||||||
: undefined,
|
|
||||||
mother:
|
|
||||||
node.member.mother && availableMembers.has(node.member.mother)
|
|
||||||
? node.member.mother.toString()
|
|
||||||
: undefined,
|
|
||||||
|
|
||||||
spouses: node.couples
|
|
||||||
?.filter((s) => availableMembers.has(s.member.id))
|
|
||||||
.map((c) => c.member.id.toString()),
|
|
||||||
children: children.map((c) => c.toString()),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
node?.down?.forEach((e) =>
|
|
||||||
treeToF3DataDownRecurse(e, array, availableMembers)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (node.couples) {
|
|
||||||
for (const c of node.couples) {
|
|
||||||
if (!availableMembers.has(c.member.id)) continue;
|
|
||||||
array.push({
|
|
||||||
data: memberData(c.member, c.couple),
|
|
||||||
id: c.member.id.toString(),
|
|
||||||
rels: {
|
|
||||||
father:
|
|
||||||
c.member.father && availableMembers.has(c.member.father)
|
|
||||||
? c.member.father.toString()
|
|
||||||
: undefined,
|
|
||||||
mother:
|
|
||||||
c.member.mother && availableMembers.has(c.member.mother)
|
|
||||||
? c.member.mother.toString()
|
|
||||||
: undefined,
|
|
||||||
spouses: [node.member.id.toString()],
|
|
||||||
children: c.down
|
|
||||||
.filter((c) => availableMembers.has(c.member.id))
|
|
||||||
.map((c) => c.member.id.toString()),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
c.down.forEach((e) =>
|
|
||||||
treeToF3DataDownRecurse(e, array, availableMembers)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
.f3 {
|
|
||||||
height: 700px;
|
|
||||||
max-height: calc(100vh - 80px);
|
|
||||||
width: 900px;
|
|
||||||
max-width: 100%;
|
|
||||||
margin: auto;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.f3 .cursor-pointer {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.f3 svg.main_svg {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
/*background-color: #3b5560;*/
|
|
||||||
color: #3b5560;
|
|
||||||
}
|
|
||||||
.f3 svg.main_svg text {
|
|
||||||
fill: currentColor;
|
|
||||||
}
|
|
||||||
.f3 rect.card-female,
|
|
||||||
.f3 .card-female .card-body-rect,
|
|
||||||
.f3 .card-female .text-overflow-mask {
|
|
||||||
fill: lightpink;
|
|
||||||
}
|
|
||||||
.f3 rect.card-male,
|
|
||||||
.f3 .card-male .card-body-rect,
|
|
||||||
.f3 .card-male .text-overflow-mask {
|
|
||||||
fill: lightblue;
|
|
||||||
}
|
|
||||||
.f3 .card-genderless .card-body-rect,
|
|
||||||
.f3 .card-genderless .text-overflow-mask {
|
|
||||||
fill: lightgray;
|
|
||||||
}
|
|
||||||
.f3 .card_add .card-body-rect {
|
|
||||||
fill: #3b5560;
|
|
||||||
stroke-width: 4px;
|
|
||||||
stroke: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.f3 g.card_add text {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
.f3 .card-main {
|
|
||||||
stroke: #000;
|
|
||||||
}
|
|
||||||
.f3 .card_family_tree rect {
|
|
||||||
transition: 0.3s;
|
|
||||||
}
|
|
||||||
.f3 .card_family_tree:hover rect {
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
.f3 .card_add_relative {
|
|
||||||
cursor: pointer;
|
|
||||||
color: #fff;
|
|
||||||
transition: 0.3s;
|
|
||||||
}
|
|
||||||
.f3 .card_add_relative circle {
|
|
||||||
fill: rgba(0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
.f3 .card_add_relative:hover {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
.f3 .card_edit.pencil_icon {
|
|
||||||
color: #fff;
|
|
||||||
transition: 0.3s;
|
|
||||||
}
|
|
||||||
.f3 .card_edit.pencil_icon:hover {
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
.f3 .card_break_link,
|
|
||||||
.f3 .link_upper,
|
|
||||||
.f3 .link_lower,
|
|
||||||
.f3 .link_particles {
|
|
||||||
transform-origin: 50% 50%;
|
|
||||||
transition: 1s;
|
|
||||||
}
|
|
||||||
.f3 .card_break_link {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
.f3 .card_break_link.closed .link_upper {
|
|
||||||
transform: translate(-140.5px, 655.6px);
|
|
||||||
}
|
|
||||||
.f3 .card_break_link.closed .link_upper g {
|
|
||||||
transform: rotate(-58deg);
|
|
||||||
}
|
|
||||||
.f3 .card_break_link.closed .link_particles {
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
.f3 .input-field input {
|
|
||||||
height: 2.5rem !important;
|
|
||||||
}
|
|
||||||
.f3 .input-field > label:not(.label-icon).active {
|
|
||||||
-webkit-transform: translateY(-8px) scale(0.8);
|
|
||||||
transform: translateY(-8px) scale(0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.f3-light .link {
|
|
||||||
stroke: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.f3-export {
|
|
||||||
position: fixed;
|
|
||||||
top: 0px;
|
|
||||||
left: 0px;
|
|
||||||
/*width: 3508px;
|
|
||||||
height: 2480px;*/
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
declare module "family-chart" {
|
|
||||||
type f3data = any;
|
|
||||||
type f3tree = any;
|
|
||||||
|
|
||||||
interface f3Rels {
|
|
||||||
spouses?: string[];
|
|
||||||
father?: string;
|
|
||||||
mother?: string;
|
|
||||||
children?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface f3DataData {
|
|
||||||
gender: "M" | "F";
|
|
||||||
avatar?: string;
|
|
||||||
dead: boolean;
|
|
||||||
birthday?: string;
|
|
||||||
deathday?: string;
|
|
||||||
first_name: string;
|
|
||||||
last_name: string;
|
|
||||||
dateOfWedding?: string;
|
|
||||||
wedding_state?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface f3Data {
|
|
||||||
id: string;
|
|
||||||
rels: f3Rels;
|
|
||||||
data: f3DataData;
|
|
||||||
}
|
|
||||||
|
|
||||||
type f3State = {
|
|
||||||
data: f3Data[];
|
|
||||||
main_id?: any;
|
|
||||||
tree?: f3tree;
|
|
||||||
node_separation?: number;
|
|
||||||
level_separation?: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface f3Update {
|
|
||||||
tree: (props) => void;
|
|
||||||
mainId: (mainId) => void;
|
|
||||||
data: (data: f3data) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface f3Store {
|
|
||||||
state: f3State;
|
|
||||||
update: f3update;
|
|
||||||
getData: () => f3data;
|
|
||||||
getTree: () => f3tree;
|
|
||||||
setOnUpdate: (cb: (props) => void) => void;
|
|
||||||
methods: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createStore(initial_state: f3State): f3Store;
|
|
||||||
|
|
||||||
function CalculateTree({
|
|
||||||
data_stash,
|
|
||||||
main_id = null,
|
|
||||||
is_vertical = true,
|
|
||||||
node_separation = 250,
|
|
||||||
level_separation = 150,
|
|
||||||
});
|
|
||||||
|
|
||||||
function d3AnimationView(p: {
|
|
||||||
store: f3Store;
|
|
||||||
cont: HTMLElement | null;
|
|
||||||
Card?: any;
|
|
||||||
});
|
|
||||||
|
|
||||||
const handlers: any;
|
|
||||||
|
|
||||||
type F3elements = {
|
|
||||||
Card: (props: {
|
|
||||||
store: f3Store;
|
|
||||||
svg: HTMLElement;
|
|
||||||
mini_tree: boolean;
|
|
||||||
link_break: boolean;
|
|
||||||
cardEditForm?: boolean;
|
|
||||||
card_dim: {
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
text_x: number;
|
|
||||||
text_y: number;
|
|
||||||
img_w: number;
|
|
||||||
img_h: number;
|
|
||||||
img_x: number;
|
|
||||||
img_y: number;
|
|
||||||
};
|
|
||||||
card_display: ((data: f3Data) => string)[];
|
|
||||||
}) => F3CardBuilder;
|
|
||||||
};
|
|
||||||
|
|
||||||
type F3CardBuilder = (p: { node; d }) => HTMLElement;
|
|
||||||
|
|
||||||
const elements: F3elements;
|
|
||||||
}
|
|
@ -13,6 +13,7 @@ import { downloadBlob } from "../../utils/files_utils";
|
|||||||
import { getTextWidth } from "../../utils/render_utils";
|
import { getTextWidth } from "../../utils/render_utils";
|
||||||
import "./simpletree.css";
|
import "./simpletree.css";
|
||||||
import "./Roboto-normal";
|
import "./Roboto-normal";
|
||||||
|
import "svg2pdf.js";
|
||||||
|
|
||||||
const FACE_WIDTH = 60;
|
const FACE_WIDTH = 60;
|
||||||
const FACE_HEIGHT = 70;
|
const FACE_HEIGHT = 70;
|
||||||
@ -92,7 +93,8 @@ function buildSimpleTreeNode(
|
|||||||
): SimpleTreeNode {
|
): SimpleTreeNode {
|
||||||
if (depth === 0) throw new Error("Too much recursion reached!");
|
if (depth === 0) throw new Error("Too much recursion reached!");
|
||||||
|
|
||||||
const lastCouple = tree.couples?.[tree.couples?.length - 1 ?? 0];
|
const lastCoupleId = tree.couples?.length ?? 1;
|
||||||
|
const lastCouple = tree.couples?.[lastCoupleId - 1];
|
||||||
|
|
||||||
// Preprocess children
|
// Preprocess children
|
||||||
let childrenToProcess = tree.down;
|
let childrenToProcess = tree.down;
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "ESNext",
|
||||||
"lib": [
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"dom",
|
"types": ["vite/client"],
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@ -20,7 +17,5 @@
|
|||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src"]
|
||||||
"src"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -13,4 +13,4 @@ if [ ! "$DRONE_COMMIT_BRANCH" == "master" ]; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd build && aws --endpoint-url https://s3.communiquons.org s3 sync . s3://geneit-app
|
cd dist && aws --endpoint-url https://s3.communiquons.org s3 sync . s3://geneit-app
|
||||||
|
1
geneit_app/vite-env.d.ts
vendored
Normal file
1
geneit_app/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
15
geneit_app/vite.config.ts
Normal file
15
geneit_app/vite.config.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import react from "@vitejs/plugin-react";
|
||||||
|
import viteTsconfigPaths from "vite-tsconfig-paths";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
// depending on your application, base can also be "/"
|
||||||
|
base: "/",
|
||||||
|
plugins: [react(), viteTsconfigPaths()],
|
||||||
|
server: {
|
||||||
|
// this ensures that the browser opens upon server start
|
||||||
|
open: true,
|
||||||
|
// this sets a default port to 3000
|
||||||
|
port: 3000,
|
||||||
|
},
|
||||||
|
});
|
1156
geneit_backend/Cargo.lock
generated
1156
geneit_backend/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -6,34 +6,34 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
log = "0.4.20"
|
log = "0.4.21"
|
||||||
env_logger = "0.10.0"
|
env_logger = "0.10.1"
|
||||||
clap = { version = "4.4.3", features = ["derive", "env"] }
|
clap = { version = "4.4.12", features = ["derive", "env"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
anyhow = "1.0.75"
|
anyhow = "1.0.75"
|
||||||
actix-web = "4.4.0"
|
actix-web = "4.5.1"
|
||||||
actix-cors = "0.6.4"
|
actix-cors = "0.7.0"
|
||||||
actix-multipart = "0.6.1"
|
actix-multipart = "0.6.1"
|
||||||
actix-remote-ip = "0.1.0"
|
actix-remote-ip = "0.1.0"
|
||||||
futures-util = "0.3.28"
|
futures-util = "0.3.29"
|
||||||
diesel = { version = "2.1.3", features = ["postgres"] }
|
diesel = { version = "2.1.4", features = ["postgres"] }
|
||||||
serde = { version = "1.0.188", features = ["derive"] }
|
serde = { version = "1.0.194", features = ["derive"] }
|
||||||
serde_json = "1.0.96"
|
serde_json = "1.0.110"
|
||||||
mailchecker = "5.0.9"
|
mailchecker = "6.0.1"
|
||||||
redis = "0.23.3"
|
redis = "0.23.3"
|
||||||
lettre = "0.10.4"
|
lettre = "0.11.2"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
bcrypt = "0.15.0"
|
bcrypt = "0.15.0"
|
||||||
light-openid = "1.0.1"
|
light-openid = "1.0.1"
|
||||||
thiserror = "1.0.48"
|
thiserror = "1.0.57"
|
||||||
serde_with = "3.1.0"
|
serde_with = "3.4.0"
|
||||||
rust_iso3166 = "0.1.10"
|
rust_iso3166 = "0.1.11"
|
||||||
rust-s3 = "0.33.0"
|
rust-s3 = "0.33.0"
|
||||||
sha2 = "0.10.7"
|
sha2 = "0.10.8"
|
||||||
image = "0.24.6"
|
image = "0.24.9"
|
||||||
uuid = { version = "1.4.1", features = ["v4"] }
|
uuid = { version = "1.6.1", features = ["v4"] }
|
||||||
httpdate = "1.0.3"
|
httpdate = "1.0.3"
|
||||||
zip = "0.6.6"
|
zip = "0.6.6"
|
||||||
mime_guess = "2.0.4"
|
mime_guess = "2.0.4"
|
||||||
tempfile = "3.8.0"
|
tempfile = "3.10.1"
|
||||||
base64 = "0.21.2"
|
base64 = "0.21.7"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM debian:bullseye-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
RUN apt-get update \
|
RUN apt-get update \
|
||||||
&& apt-get install -y libcurl4 libpq5 \
|
&& apt-get install -y libcurl4 libpq5 \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user