Kanban-Board: Ticket-Nummer auf den einzelnen Zetteln anzeigen (nicht am Stapel)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
fds2610-bot 2026-09-21 15:05:42 +02:00
parent 806fcdfb43
commit 9ce938c45b
4 changed files with 48 additions and 1 deletions

View file

@ -212,6 +212,17 @@ header.office-header h1 {
stroke: rgba(15, 23, 42, 0.35);
}
/* Issue #91: Ticket-Nummer auf dem Zettel; heller Rand haelt sie auf dunklen Projektfarben lesbar. */
.note-number {
fill: #0f172a;
font-size: 16px;
font-weight: 700;
paint-order: stroke;
stroke: rgba(255, 255, 255, 0.8);
stroke-width: 3px;
pointer-events: none;
}
.note-stack-badge {
fill: var(--color-note-stack-badge);
stroke: #92400e;

View file

@ -299,6 +299,16 @@ function renderBoard(ticketCounts, groupedTickets, onColumnClick, onReloadClick)
column.noteColors[i]
)
);
if (column.noteNumbers?.[i]) {
colGroup.appendChild(
text(column.noteNumbers[i], {
x: noteX + NOTE_W / 2,
y: noteY + NOTE_H / 2 + 6,
class: "note-number",
"text-anchor": "middle",
})
);
}
noteY += NOTE_STEP_Y;
}

View file

@ -50,7 +50,7 @@ export const NOTE_STACK_LAYERS = 3;
* @param {Record<string, Array<object>>} groupedTickets - Tickets je Spalte in derselben
* Reihenfolge wie gezaehlt (siehe groupTicketsByColumn()).
* @returns {Array<{key: string, label: string, count: number, visibleNotes: number,
* stackCount: number, noteColors: Array<string|null>, stackColors: Array<string>}>}
* stackCount: number, noteColors: Array<string|null>, noteNumbers: Array<string|null>, stackColors: Array<string>}>}
*/
export function buildBoardColumnLayout(ticketCounts = {}, groupedTickets = {}) {
return BOARD_COLUMNS.map(({ key, label }) => {
@ -64,6 +64,7 @@ export function buildBoardColumnLayout(ticketCounts = {}, groupedTickets = {}) {
visibleNotes,
stackCount,
noteColors: noteColors(tickets.slice(0, visibleNotes), visibleNotes),
noteNumbers: noteNumbers(tickets.slice(0, visibleNotes), visibleNotes),
stackColors: stackCount > 0 ? stackLayerColors(tickets.slice(visibleNotes)) : [],
});
@ -85,6 +86,15 @@ function noteColors(tickets, length) {
return Array.from({ length }, (_, i) => tickets[i]?.color ?? null);
}
// Issue #91: Ticket-Nummer auf dem Zettel (nur Codeberg-Tickets haben eine); null bei
// Tickets ohne Nummer und im Demo-Modus. Der Stapel zeigt bewusst keine Nummern.
function noteNumbers(tickets, length) {
return Array.from({ length }, (_, i) => {
const ticket = tickets[i];
return ticket?.source === "codeberg" && ticket.external_ref ? `#${ticket.external_ref}` : null;
});
}
// Ein Stapel steht fuer viele Tickets, aber nur fuer NOTE_STACK_LAYERS Zettel: er zeigt
// deshalb die ersten verschiedenen Projektfarben darin, statt eine davon dreimal zu
// wiederholen. So ist auch an "Done" (immer ein Stapel) zu sehen, aus welchen Projekten die

View file

@ -112,6 +112,22 @@ test("buildBoardColumnLayout faerbt jeden Zettel in der Farbe seines Projekts (I
assert.deepEqual(bereit.noteColors, ["#fbca04", "#28a745", "#fbca04"]);
});
test("buildBoardColumnLayout liefert Ticket-Nummern fuer Zettel, nicht fuer den Stapel (Issue #91)", () => {
const tickets = Array.from({ length: 7 }, (_, i) => ({
column_name: "bereit",
source: "codeberg",
external_ref: String(i + 1),
}));
tickets[1] = { column_name: "bereit", source: "api" };
const bereit = buildBoardColumnLayout(
aggregateTicketsByColumn(tickets),
groupTicketsByColumn(tickets)
).find((c) => c.key === "bereit");
assert.deepEqual(bereit.noteNumbers, ["#1", null, "#3", "#4"]);
assert.equal(bereit.stackCount, 3);
});
test("buildBoardColumnLayout laesst Zettel ohne Projektfarbe ungefaerbt", () => {
const tickets = [{ column_name: "qa" }, { column_name: "qa", color: "#0366d6" }];
const qa = buildBoardColumnLayout(