diff --git a/frontend/src/components/git/ForgejoIssuesTable.tsx b/frontend/src/components/git/ForgejoIssuesTable.tsx index 431f63f..e43e74d 100644 --- a/frontend/src/components/git/ForgejoIssuesTable.tsx +++ b/frontend/src/components/git/ForgejoIssuesTable.tsx @@ -13,7 +13,44 @@ import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { DataTable } from "@/components/tables/DataTable"; import { CloseForgejoIssueDialog } from "@/components/git/CloseForgejoIssueDialog"; -import type { ForgejoIssue } from "@/lib/api-forgejo"; +import type { ForgejoIssue, ForgejoIssueLabel } from "@/lib/api-forgejo"; + +/** Normalize a Forgejo label color to a valid 6-char hex string or null. */ +function normalizeLabelColor(raw: string | null | undefined): string | null { + if (!raw) return null; + const hex = raw.replace(/^#+/, ""); + return /^[0-9a-fA-F]{3,6}$/.test(hex) ? `#${hex.padEnd(6, "0")}` : null; +} + +/** Return white or dark text based on WCAG relative luminance of a hex background. */ +function labelTextColor(hex: string): string { + const h = hex.replace("#", ""); + const r = parseInt(h.slice(0, 2), 16) / 255; + const g = parseInt(h.slice(2, 4), 16) / 255; + const b = parseInt(h.slice(4, 6), 16) / 255; + const lin = (c: number) => + c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4); + const L = 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b); + return L > 0.179 ? "#1a1a1a" : "#ffffff"; +} + +function LabelChip({ label }: { label: ForgejoIssueLabel }) { + const bg = normalizeLabelColor(label.color); + return ( + + {label.name} + + ); +} export type ForgejoIssuesTableProps = { issues: ForgejoIssue[]; @@ -108,26 +145,17 @@ export function ForgejoIssuesTable({ cell: ({ row }) => { const labels = row.original.labels; if (!labels || labels.length === 0) return null; + const visible = labels.slice(0, 3); + const overflow = labels.length - visible.length; return ( -
- {labels - .slice(0, 3) - .map((label: Record, i: number) => ( - - {String(label.name || "")} - - ))} - {labels.length > 3 && ( - +{labels.length - 3} +
+ {visible.map((label, i) => ( + + ))} + {overflow > 0 && ( + + +{overflow} + )}
); diff --git a/frontend/src/lib/api-forgejo.ts b/frontend/src/lib/api-forgejo.ts index 5794349..2a5833f 100644 --- a/frontend/src/lib/api-forgejo.ts +++ b/frontend/src/lib/api-forgejo.ts @@ -251,6 +251,13 @@ export async function validateRepository( } // Forgejo Issue types +export interface ForgejoIssueLabel { + id?: number | null; + name: string; + color: string; + description?: string | null; +} + export interface ForgejoIssue { id: string; organization_id: string; @@ -260,7 +267,7 @@ export interface ForgejoIssue { body_preview: string | null; state: string; is_pull_request: boolean; - labels: Record[]; + labels: ForgejoIssueLabel[]; assignees: Record[]; author: string; html_url: string;