diff --git a/apps/app/src/lib/graph/PersonNode.svelte b/apps/app/src/lib/graph/PersonNode.svelte
index 8fa3695..65e0d74 100644
--- a/apps/app/src/lib/graph/PersonNode.svelte
+++ b/apps/app/src/lib/graph/PersonNode.svelte
@@ -27,7 +27,7 @@
{#if isConnecting && isTarget}
{#if isConnecting && isTarget}
{
+ this.setNode(node.id, { width: nodeWidth, height: nodeHeight});
+ });
+
+ edges.forEach((edge) => {
+ if (edge.data?.type === 'child') {
+ this.setEdge(edge.source, edge.target);
+ }
+ });
+
+ dagre.layout(this);
+
+ edges.forEach((edge) => {
+ if (edge.data?.type === 'parent' || edge.data?.type === 'sibling') {
+ this.setEdge(edge.source, edge.target);
+ }
+ });
+
+ edges.forEach((edge) => {
+ if (edge.data?.type === 'spouse') {
+ const sourceNode = this.node(edge.source);
+ const targetNode = this.node(edge.target);
+
+ if (!sourceNode || !targetNode) {
+ return;
+ }
+
+ const padding = 50; // distance between spouse and source
+ const spouseWidth = nodeWidth;
+
+ const existingNodesAtLevel = nodes
+ .map((n) => ({ id: n.id, pos: this.node(n.id) }))
+ .filter(({ pos }) => Math.abs(pos.y - sourceNode.y) < nodeHeight / 2); // same horizontal band
+
+ // Collect taken x ranges
+ const takenXRanges = existingNodesAtLevel.map(({ pos }) => ({
+ from: pos.x - spouseWidth / 2,
+ to: pos.x + spouseWidth / 2
+ }));
+
+ // Try placing spouse to the right
+ let desiredX = sourceNode.x + nodeWidth + padding;
+
+ // Check for collision
+ const collides = (x: number) => {
+ return takenXRanges.some(({ from, to }) => x > from && x < to);
+ };
+
+ // If right side collides, try left
+ if (collides(desiredX)) {
+ desiredX = sourceNode.x - (nodeWidth + padding);
+ }
+
+ // If both sides collide, push right until free
+ while (collides(desiredX)) {
+ desiredX += nodeWidth + padding;
+ }
+
+ targetNode.x = desiredX;
+ targetNode.y = sourceNode.y;
+ }
+ });
+
+ const layoutedNodes = nodes.map((node) => {
+ const nodeWithPosition = this.node(node.id);
+ node.targetPosition = isHorizontal ? Position.Left : Position.Top;
+ node.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
+
+ // We are shifting the dagre node position (anchor=center center) to the top left
+ // so it matches the React Flow node anchor point (top left).
+ return {
+ ...node,
+ type: 'personNode',
+ position: {
+ x: nodeWithPosition.x - nodeWidth / 2,
+ y: nodeWithPosition.y - nodeHeight / 2
+ }
+ };
+ });
+
+ return { Nodes: layoutedNodes, Edges: edges };
+ }
+}
diff --git a/apps/app/src/lib/graph/model.ts b/apps/app/src/lib/graph/model.ts
index ea9ad8b..394e68f 100644
--- a/apps/app/src/lib/graph/model.ts
+++ b/apps/app/src/lib/graph/model.ts
@@ -1,4 +1,7 @@
-import type { Node, Edge } from '@xyflow/svelte';
+import type { Node, Edge, NodeTypes } from '@xyflow/svelte';
+import PersonNode from './PersonNode.svelte';
+
+export const nodeTypes: NodeTypes = { personNode: PersonNode };
export type NodeMenu = {
onClick: () => void;
@@ -14,6 +17,6 @@ export type NodeMenu = {
};
export type Layout = {
- Nodes: Array;
- Edges: Array;
-}
\ No newline at end of file
+ Nodes: Array;
+ Edges: Array;
+};
diff --git a/apps/app/src/routes/+page.server.ts b/apps/app/src/routes/+page.server.ts
index 01918da..c017f1e 100644
--- a/apps/app/src/routes/+page.server.ts
+++ b/apps/app/src/routes/+page.server.ts
@@ -1,6 +1,7 @@
-import { fail, redirect } from '@sveltejs/kit';
-import { client } from '$lib/api/client';
-import type { RequestEvent } from './$types';
+import { redirect } from '@sveltejs/kit';
+import { parseFamilyTree } from '$lib/graph/fetch_family_tree';
+import type { components } from '$lib/api/api.gen';
+import type { RequestEvent } from './$types';
import { browser } from '$app/environment';
export async function load(event: RequestEvent) {
@@ -13,19 +14,17 @@ export async function load(event: RequestEvent) {
return {};
}
- const response = await client
- .GET('/family-tree-with-spouses', {
- params: {
- header: { 'X-User-ID': event.locals.session.userId },
- }
- })
+ const response = await event.fetch('/api/family_tree?with_out_spouse=false', {
+ method: 'GET'
+ });
- if (response.response.status === 200) {
- return response.data;
- } else {
- return fail(response.response.status, {
- message: response.error?.msg || 'An error occurred'
- });
+ if (response.status !== 200) {
+ throw new Error(await response.text());
}
+ const data = (await response.json()) as components['schemas']['FamilyTree'];
+
+ let layout = parseFamilyTree(data)
+
+ return layout;
}
diff --git a/apps/app/src/routes/+page.svelte b/apps/app/src/routes/+page.svelte
index 38e0f9d..333761c 100644
--- a/apps/app/src/routes/+page.svelte
+++ b/apps/app/src/routes/+page.svelte
@@ -1,8 +1,14 @@
@@ -115,6 +146,8 @@
{nodeTypes}
fitView
onlyRenderVisibleElements
+ connectionLineType={ConnectionLineType.SmoothStep}
+ defaultEdgeOptions={{ type: 'smoothstep' }}
>
@@ -127,7 +160,13 @@
/>
{/if}
{#if createPerson}
-
+ {
+ createPerson = false;
+ }}
+ relationshipStartID={relationshipStart}
+ >
{/if}
{#if openPersonMenu !== undefined}
diff --git a/apps/app/src/routes/page.stories.svelte b/apps/app/src/routes/page.stories.svelte
deleted file mode 100644
index 02908c5..0000000
--- a/apps/app/src/routes/page.stories.svelte
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-