- /npcs/family/{family} (app_npc_family) renders a parent->child tree for
everyone sharing that family name; co-parents who are both in the family
are paired into one couple node, elders first
- FamilyTreeBuilder does the flat-list -> tree assembly
- the NPC detail page's family name (subtitle + Details row) now links there
- .family-tree CSS: nested list with connector lines, small portrait avatars
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
72 lines
3.7 KiB
Twig
72 lines
3.7 KiB
Twig
{% extends 'base.html.twig' %}
|
|
|
|
{% block title %}{{ npc.name }} · NPC's · dmtools{% endblock %}
|
|
|
|
{% block body %}
|
|
<div class="d-flex justify-content-between align-items-start mb-3 gap-3">
|
|
<div class="d-flex gap-3">
|
|
<img src="{{ asset(npc.portraitPath) }}" alt="Portrait of {{ npc.name }}"
|
|
width="110" height="140"
|
|
class="rounded border object-fit-cover flex-shrink-0"
|
|
onerror="this.remove()">
|
|
<div>
|
|
<a href="{{ path('app_npcs') }}" class="text-decoration-none small">← All NPC's</a>
|
|
<h1 class="h3 mb-0">{{ npc.name }}</h1>
|
|
<span class="text-body-secondary">
|
|
<a href="{{ path('app_npc_family', {family: npc.family}) }}">{{ npc.family }}</a>{% if npc.occupation %} · {{ npc.occupation }}{% endif %}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<a href="{{ path('app_npc_edit', {id: npc.id}) }}" class="btn btn-outline-secondary btn-sm">Edit details</a>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
<div class="col-lg-5">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header">Details</div>
|
|
<div class="card-body">
|
|
<dl class="row mb-0">
|
|
<dt class="col-5">Family</dt>
|
|
<dd class="col-7"><a href="{{ path('app_npc_family', {family: npc.family}) }}">{{ npc.family }}</a> family tree</dd>
|
|
<dt class="col-5">Age</dt><dd class="col-7">{{ npc.age ?? '—' }}</dd>
|
|
<dt class="col-5">Race</dt><dd class="col-7">{{ npc.race.value }}</dd>
|
|
<dt class="col-5">Occupation</dt><dd class="col-7">{{ npc.occupation ?? '—' }}</dd>
|
|
<dt class="col-5">Village status</dt><dd class="col-7">{{ npc.villageStatus.value }}</dd>
|
|
<dt class="col-5">Blood cart</dt><dd class="col-7">{{ npc.bloodCartInvolvement.value }}</dd>
|
|
<dt class="col-5">Mystery importance</dt><dd class="col-7">{{ npc.mysteryImportance.value }}</dd>
|
|
<dt class="col-5">Father</dt>
|
|
<dd class="col-7">
|
|
{% if npc.father %}<a href="{{ path('app_npc_show', {id: npc.father.id}) }}">{{ npc.father.name }}</a>{% else %}—{% endif %}
|
|
</dd>
|
|
<dt class="col-5">Mother</dt>
|
|
<dd class="col-7">
|
|
{% if npc.mother %}<a href="{{ path('app_npc_show', {id: npc.mother.id}) }}">{{ npc.mother.name }}</a>{% else %}—{% endif %}
|
|
</dd>
|
|
{% if npc.relationships %}
|
|
<dt class="col-5">Relationships / Notes</dt>
|
|
<dd class="col-7 mb-0">{{ npc.relationships|nl2br }}</dd>
|
|
{% endif %}
|
|
</dl>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-7">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<span>Game notes</span>
|
|
{% if npc.updatedAt %}
|
|
<span class="text-body-secondary small fw-normal">saved {{ npc.updatedAt|date('Y-m-d H:i') }}</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="card-body">
|
|
{{ form_start(form) }}
|
|
{{ form_row(form.gameNotes) }}
|
|
<button class="btn btn-primary mt-2">Save notes</button>
|
|
{{ form_end(form) }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|