feat: implement deterministic color-coding for licence type badges and update default scope badge style

This commit is contained in:
estifanos
2026-08-28 11:33:07 +00:00
parent 664558146d
commit c4b903bc49

View File

@@ -46,6 +46,37 @@ const GLOBAL_ONLY = 'GLOBAL';
const SEARCH_DEBOUNCE_MS = 300;
/**
* Badge colours for licence types.
*
* Red and yellow are left out on purpose: they read as a problem, and a
* licence type is not one. Green is out too — it means verified elsewhere in
* the backoffice.
*/
const SCOPE_COLORS = [
'grape',
'violet',
'indigo',
'cyan',
'teal',
'pink',
'orange',
'blue',
];
/**
* The same licence type keeps the same colour on every row and every visit —
* derived from its id rather than its position, so a colour is something the
* eye can learn instead of a lottery per render.
*/
function scopeColor(licenseTypeId: string): string {
let hash = 0;
for (let i = 0; i < licenseTypeId.length; i += 1) {
hash = (hash * 31 + licenseTypeId.charCodeAt(i)) % 997;
}
return SCOPE_COLORS[hash % SCOPE_COLORS.length];
}
/** `application/vnd.openxmlformats-…-document` is nobody's idea of a column. */
const MIME_LABELS: Record<string, string> = {
'application/pdf': 'PDF',
@@ -168,13 +199,15 @@ export function PersonalDocumentsCard() {
label: t('certReq.doc.scope', 'Applies to'),
cell: ({ row }) =>
row.original.scope.length === 0 ? (
<Badge size="sm" variant="light" color="blue">
// Filled, where a licence type is outlined: "every licence" is a
// different kind of answer, not one more item in the same list.
<Badge size="sm" variant="filled" color="gray">
{t('certReq.doc.scopeAll', 'All licences')}
</Badge>
) : (
<Group gap={4}>
{row.original.scope.map((id) => (
<Badge key={id} size="sm" variant="outline" color="grape">
<Badge key={id} size="sm" variant="light" color={scopeColor(id)}>
{typeName(id)}
</Badge>
))}