Ajout : - e2e/csp_preview.spec.js — apercu CSP strict SANS deployer : le build officiel @alpinejs/csp (e2e/fixtures/alpine.csp.js, 0 eval) est servi a la place de alpine.min.js par interception Playwright ; toute expression que le parseur maison ne digere pas = pageerror (filet). Premiere surface VERTE : library (composant lie, icones SVG via Alpine.effect, recherche ouverte + focalisee, 0 erreur). Changed : - 16 composants x-data="fn()" enregistres via Alpine.data (registre = seule resolution du build CSP, probe « Undefined variable » ; scripts classiques executes pendant le parsing => alpine:init toujours joint) : appState, libraryPage, workspacesPage, editorState, board x4, settings/import/table_view/team_load/trash/workspace/welcome/accounts/ card_detail. - base.html (shell) migre : x-effect document.* -> syncSidebarClass(), $nextTick(arrow) -> initSidebarSort(), window.FlowDeck.* -> fdCreatePage/fdCreateFolder/fdGwRefresh, Object.keys/Math.min/ window.innerWidth dans x-for et :style -> sidebarSections()/ sectionMenuPos() — tout = simple appel de methode. - x-html restants du shell -> x-init + Alpine.effect : icone agent, carte projet, library x3 ; recherche library -> toggleSearch() (Alpine.nextTick) ; openMoveSelected() pour Object.keys en expression. - eslint : 70 warnings -> 0/0 (globals getCsrf depuis A38 ph1, /* exported openCardDetail */ + /* global owner, repo */, 3 ;; residuels). suite **1093/1093** · ruff OK · eslint 0/0 · E2E 3/3 (csp_preview + smoke x2) · docs a jour (ROADMAP ph3 LOT 1, CHANGELOG, WORKLOAD, VERSION)
56 lines
2.4 KiB
HTML
56 lines
2.4 KiB
HTML
{% from '_icons.html' import fd_icon %}
|
|
<!-- Team Load — Notion-style stacked bar chart -->
|
|
<div id="view-content" x-data="teamLoad()" class="team-load">
|
|
{% for member in team_data %}
|
|
<div class="team-member">
|
|
<div class="team-count">{{ member.total }}</div>
|
|
<div class="team-bar-stack" style="height: {{ member.total * 32 }}px; min-height: 40px;">
|
|
{% if member.complete > 0 %}
|
|
<div class="team-bar-segment done"
|
|
style="height: {{ (member.complete / member.total * 100)|round }}%;"
|
|
title="Complete: {{ member.complete }}"></div>
|
|
{% endif %}
|
|
{% if member.progress > 0 %}
|
|
<div class="team-bar-segment progress"
|
|
style="height: {{ (member.progress / member.total * 100)|round }}%;"
|
|
title="In progress: {{ member.progress }}"></div>
|
|
{% endif %}
|
|
{% if member.todo > 0 %}
|
|
<div class="team-bar-segment todo"
|
|
style="height: {{ (member.todo / member.total * 100)|round }}%;"
|
|
title="To-do: {{ member.todo }}"></div>
|
|
{% endif %}
|
|
</div>
|
|
<div class="team-avatar" title="{{ member.name }}">{{ member.initial }}</div>
|
|
<div style="font-size:12px; color:var(--text-secondary); text-align:center; max-width:60px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
|
|
{{ member.name }}
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
{% if not team_data %}
|
|
<div style="text-align:center; padding:48px; width:100%; color:var(--text-dim);">
|
|
<div style="margin-bottom:16px;">{{ fd_icon("users",48) }}</div>
|
|
<h3>No team data</h3>
|
|
<p style="margin-top:8px;">Assign issues to team members to see their workload.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="status-legend" style="justify-content:center; padding: 0 24px 24px;">
|
|
<div class="legend-item"><span class="legend-dot" style="background:var(--blue);"></span> To-do</div>
|
|
<div class="legend-item"><span class="legend-dot" style="background:var(--orange);"></span> In progress</div>
|
|
<div class="legend-item"><span class="legend-dot" style="background:var(--green);"></span> Complete</div>
|
|
</div>
|
|
|
|
<script nonce="{{ csp_nonce() }}">
|
|
// A20 phase 3 : registre Alpine.data (le build CSP ne résout pas les
|
|
|
|
// globales window — probe « Undefined variable: teamLoad »).
|
|
|
|
document.addEventListener('alpine:init', function () { Alpine.data('teamLoad', teamLoad); });
|
|
function teamLoad() {
|
|
return {};
|
|
}
|
|
</script>
|