- Inventaire exhaustif des 13 noms `function NAME` définis 2+ fois (templates + static/js) avec scan de profondeur de brace (strings, comments, backticks gérés) : 12 sont déjà scopés dans des IIFEs depuis A27 (escHtml/flush/emit/setMeta/initials/up/esc/show/close…) — aucun conflit de page possible. - Seul doublon GLOBALE = openCardDetail (corps byte-identiques ×2 dans board_fragment + detailed_board, fragments de vues mutuellement exclusifs) → dédupliquée vers static/js/app.js, 2 copies supprimées ; les onclick/@click des deux fragments appellent la même définition (owner/repo globaux fournis par board.js au moment du clic). - test_no_duplicate_global_functions : garde-fou 0-doublon entre templates et static/js (scanner naïf, plafond ponytail commenté). Reste A38 : méthodes jumelles library/local_workspace (~9-21 noms communs, corps divergents) → fusion workspace-tree.js reportée (réconciliation sans E2E, même logique que A39/A20). suite **1093/1093** · ruff OK · node --check vert · docs à jour
63 lines
3.0 KiB
HTML
63 lines
3.0 KiB
HTML
<!-- Detailed Board — Notion style with all card properties visible -->
|
|
<div id="view-content" class="kanban-container" x-data="kanbanBoard()">
|
|
{% set statuses = [('todo', 'To-do', 'var(--gray)'), ('progress', 'In progress', 'var(--blue)'), ('done', 'Complete', 'var(--green)')] %}
|
|
{% for status, label, color in statuses %}
|
|
<div class="kanban-column">
|
|
<div class="kanban-column-header">
|
|
<span class="col-dot" style="background: {{ color }};"></span>
|
|
{{ label }}
|
|
<span class="col-count">{{ groups|map(attribute='counts')|map(attribute=status)|sum }}</span>
|
|
</div>
|
|
{% for group in groups %}
|
|
<div class="kanban-group-header" @click="toggleGroup('{{ group.id }}')">
|
|
<span class="group-chevron" :class="{ 'open': !collapsedGroups.includes('{{ group.id }}') }">▶</span>
|
|
{{ group.name }}
|
|
<span class="group-count">{{ group.counts[status] }}</span>
|
|
</div>
|
|
<div class="kanban-cards" x-show="!collapsedGroups.includes('{{ group.id }}')"
|
|
data-group="{{ group.id }}" data-status="{{ status }}">
|
|
{% for card in group.cards if card.status == status %}
|
|
<div class="kanban-card card-status-{{ card.status }}"
|
|
data-card-id="{{ card.id }}" style="padding: 14px;">
|
|
<div class="card-header">
|
|
<span class="card-icon">{{ card.icon }}</span>
|
|
<span class="card-title" style="font-size:14px; font-weight:600;">{{ card.title }}</span>
|
|
</div>
|
|
<div class="card-meta" style="flex-direction:column; gap:6px; margin-top:10px;">
|
|
{% if card.assignee %}
|
|
<div class="card-assignee">
|
|
<div class="card-avatar" style="width:22px; height:22px; font-size:11px;">{{ card.assignee[0] }}</div>
|
|
<span style="font-size:13px;">{{ card.assignee }}</span>
|
|
</div>
|
|
{% endif %}
|
|
{% if card.tag %}
|
|
<div>
|
|
<span class="card-tag" style="background:{{ card.tag_color }}20; color:{{ card.tag_color }}; font-size:12px; padding:2px 8px;">
|
|
{{ card.tag }}
|
|
</span>
|
|
</div>
|
|
{% endif %}
|
|
{% if card.due_date %}
|
|
<div class="card-date">
|
|
<span>{{ fd_icon("calendar",14) }}</span>
|
|
<span style="font-size:12px;">{{ card.due_date }}</span>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
<div style="position:absolute; top:8px; right:8px; opacity:0; transition:opacity 0.15s;"
|
|
onmouseover="this.style.opacity='1'" onmouseout="this.style.opacity='0'"
|
|
onclick="event.stopPropagation(); openCardDetail('{{ card.id }}')">
|
|
<button class="item-action-btn" title="Open detail">↗</button>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
<button class="kanban-new-card" @click="newCard('{{ group.id }}', '{{ status }}')">+ New page</button>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<!-- A38 : openCardDetail vit dans static/js/app.js (dedupliquee,
|
|
corps identique x2 dans ces deux fragments de vue) -->
|