Graph de famille en frontend (#820)

* Remove graphviz and use cytoscape.js instead

* Frontend generated graphs
* Make installation easier and faster
* Better user experience
* Family api and improved interface
* Fix url history when using 0, improve button selection and reset reverse with reset button
* Use klay layout
* Add js translations and apply review comments
This commit is contained in:
Bartuccio Antoine
2024-09-17 12:10:06 +02:00
committed by GitHub
parent bf96d8a10c
commit f624b7c66d
29 changed files with 1332 additions and 684 deletions

View File

@ -112,7 +112,7 @@
{% macro delete_godfather(user, profile, godfather, is_father) %}
{% if user == profile or user.is_root or user.is_board_member %}
<a href="{{ url("core:user_godfathers_delete", user_id=profile.id, godfather_id=godfather.id, is_father=is_father) }}">{% trans %}Delete{% endtrans %}</a>
<a class="delete" href="{{ url("core:user_godfathers_delete", user_id=profile.id, godfather_id=godfather.id, is_father=is_father) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
{% endmacro %}

View File

@ -11,14 +11,20 @@
{% block content %}
<div class="container">
<a href="{{ url('core:user_godfathers_tree_pict', user_id=profile.id) }}?family">
{% trans %}Show family picture{% endtrans %}
</a>
{% if godchildren or godfathers %}
<a
href="{{ url('core:user_godfathers_tree', user_id=profile.id) }}"
class="btn btn-blue"
id="family-tree-link"
>
{% trans %}Show family tree{% endtrans %}
</a>
{% endif %}
<h4>{% trans %}Godfathers / Godmothers{% endtrans %}</h4>
{% if profile.godfathers.exists() %}
{% if godfathers %}
<ul class="users">
{% for u in profile.godfathers.all() %}
{% for u in godfathers %}
<li class="users-card">
<a href="{{ url('core:user_godfathers', user_id=u.id) }}" class="mini_profile_link">
{{ u.get_mini_item() | safe }}
@ -28,17 +34,14 @@
{% endfor %}
</ul>
<a href="{{ url('core:user_godfathers_tree', user_id=profile.id) }}">
{% trans %}Show ancestors tree{% endtrans %}
</a>
{% else %}
<p>{% trans %}No godfathers / godmothers{% endtrans %}
{% endif %}
<h4>{% trans %}Godchildren{% endtrans %}</h4>
{% if profile.godchildren.exists() %}
{% if godchildren %}
<ul class="users">
{% for u in profile.godchildren.all() %}
{% for u in godchildren %}
<li class="users-card">
<a href="{{ url('core:user_godfathers', user_id=u.id) }}" class="mini_profile_link">
{{ u.get_mini_item()|safe }}
@ -47,10 +50,6 @@
</li>
{% endfor %}
</ul>
<a href="{{ url('core:user_godfathers_tree', user_id=profile.id) }}?descent">
{% trans %}Show descent tree{% endtrans %}
</a>
{% else %}
<p>{% trans %}No godchildren{% endtrans %}
{% endif %}

View File

@ -1,54 +1,105 @@
{% extends "core/base.jinja" %}
{% set depth_min=0 %}
{% set depth_max=10 %}
{%- block additional_css -%}
<link rel="stylesheet" href="{{ scss('user/user_godfathers.scss') }}">
{%- endblock -%}
{% block additional_js %}
<script src="{{ static("vendored/cytoscape/cytoscape.min.js") }}" defer></script>
<script src="{{ static("vendored/cytoscape/cytoscape-cxtmenu.min.js") }}" defer></script>
<script src="{{ static("vendored/cytoscape/klay.min.js") }}" defer></script>
<script src="{{ static("vendored/cytoscape/cytoscape-klay.min.js") }}" defer></script>
<script src="{{ static("user/js/family_graph.js") }}" defer></script>
{% endblock %}
{% block title %}
{% if param == "godchildren" %}
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s godchildren{% endtrans %}
{% else %}
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s godfathers{% endtrans %}
{% endif %}
{% trans user_name=profile.get_display_name() %}{{ user_name }}'s family tree{% endtrans %}
{% endblock %}
{% macro display_members_list(user) %}
{% if user.__getattribute__(param).exists() %}
<ul>
{% for u in user.__getattribute__(param).all() %}
<li>
<a href="{{ url("core:user_godfathers", user_id=u.id) }}">
{{ u.get_short_name() }}
</a>
{% if u in members_set %}
{% trans %}Already seen (check above){% endtrans %}
{% else %}
{{ members_set.add(u) or "" }}
{{ display_members_list(u) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endif %}
{% endmacro %}
{% block content %}
<p><a href="{{ url("core:user_godfathers", user_id=profile.id) }}">
{% trans %}Back to family{% endtrans %}</a></p>
{% if profile.__getattribute__(param).exists() %}
{% if param == "godchildren" %}
<p><a href="{{ url("core:user_godfathers_tree_pict", user_id=profile.id) }}?descent">
{% trans %}Show a picture of the tree{% endtrans %}</a></p>
<h4>{% trans u=profile.get_short_name() %}Descent tree of {{ u }}{% endtrans %}</h4>
{% else %}
<p><a href="{{ url("core:user_godfathers_tree_pict", user_id=profile.id) }}?ancestors">
{% trans %}Show a picture of the tree{% endtrans %}</a></p>
<h4>{% trans u=profile.get_short_name() %}Ancestors tree of {{ u }}{% endtrans %}</h4>
{% endif %}
{{ members_set.add(profile) or "" }}
{{ display_members_list(profile) }}
{% else %}
{% if param == "godchildren" %}
<p>{% trans %}No godchildren{% endtrans %}
{% else %}
<p>{% trans %}No godfathers / godmothers{% endtrans %}
{% endif %}
{% endif %}
<div x-data="graph" :aria-busy="loading">
<div class="graph-toolbar">
<div class="toolbar-column">
<div class="toolbar-input">
<label for="godfather-depth-input">
{% trans min=depth_min, max=depth_max %}Max godfather depth between {{ min }} and {{ max }}{% endtrans %}
</label>
<span class="depth-choice">
<button
@click="godfathers_depth--"
:disabled="godfathers_depth <= {{ depth_min }}"
><i class="fa fa-minus fa-xs"></i></button>
<input
x-model="godfathers_depth"
x-ref="godfather_depth_input"
type="number"
name="godfathers_depth"
id="godfather-depth-input"
min="{{ depth_min }}"
max="{{ depth_max }}"
/>
<button
@click="godfathers_depth++"
:disabled="godfathers_depth >= {{ depth_max }}"
><i class="fa fa-plus"
></i></button>
</span>
</div>
<div class="toolbar-input">
<label for="godchild-depth-input">
{% trans min=depth_min, max=depth_max %}Max godchildren depth between {{ min }} and {{ max }}{% endtrans %}
</label>
<span class="depth-choice">
<button
@click="godchildren_depth--"
:disabled="godchildren_depth <= {{ depth_min }}"
><i
class="fa fa-minus fa-xs"
></i></button>
<input
x-model="godchildren_depth"
type="number"
name="godchildren_depth"
id="godchild-depth-input"
min="{{ depth_min }}"
max="{{ depth_max }}"
/>
<button
@click="godchildren_depth++"
:disabled="godchildren_depth >= {{ depth_max }}"
><i class="fa fa-plus"
></i></button>
</span>
</div>
</div>
<div class="toolbar-column">
<div class="toolbar-input">
<label for="reverse-checkbox">{% trans %}Reverse{% endtrans %}</label>
<input x-model="reverse" type="checkbox" name="reverse" id="reverse-checkbox">
</div>
<button class="btn btn-grey" @click="reset">
{% trans %}Reset{% endtrans %}
</button>
<button class="btn btn-grey" @click="screenshot">
<i class="fa fa-camera"></i>
{% trans %}Save{% endtrans %}
</button>
</div>
</div>
<div x-ref="graph" class="graph"></div>
</div>
<script>
const api_url = "{{ api_url }}";
const active_user = "{{ object.id }}"
const depth_min = {{ depth_min }};
const depth_max = {{ depth_max }};
</script>
{% endblock %}