diff --git a/web/src/app/app/crm/tasks/page.tsx b/web/src/app/app/crm/tasks/page.tsx
index 3940b3117..0198be9dd 100644
--- a/web/src/app/app/crm/tasks/page.tsx
+++ b/web/src/app/app/crm/tasks/page.tsx
@@ -799,15 +799,22 @@ function TaskTypeTag({
if (!type) {
return compact ? null : —;
}
- const color = taskTypeColor(type, types);
+ // Render the type as a colour-tinted chip (faint background in the type's
+ // colour + a solid dot + text in the colour) so the colour is unmistakable,
+ // not a single 8px dot. Done tasks fade to grey. Colours are DB-enforced hex.
+ const color = done ? "#94a3b8" : taskTypeColor(type, types);
return (
-
+
{type}
);
@@ -1044,12 +1051,14 @@ function TypeFacet({
closeOnSelect={false}
icon={
}
>
- {t.name}
+
+ {t.name}
+
))
)}
diff --git a/web/src/components/app/crm/taskTypes.tsx b/web/src/components/app/crm/taskTypes.tsx
index 73dabe93d..2e0afe9e4 100644
--- a/web/src/components/app/crm/taskTypes.tsx
+++ b/web/src/components/app/crm/taskTypes.tsx
@@ -18,5 +18,13 @@ export function taskTypeColor(
types: { name: string; color: string }[],
): string {
if (!name) return "#94a3b8";
- return types.find((t) => t.name === name)?.color ?? "#94a3b8";
+ const key = name.trim().toLowerCase();
+ const found = types.find((t) => t.name.trim().toLowerCase() === key)?.color;
+ if (found) return found;
+ // No matching type (renamed/removed, or a case/whitespace mismatch between the
+ // task's stored type string and the type list): hash the name into the palette
+ // so it still gets a stable, DISTINCT colour instead of one grey for everything.
+ let h = 0;
+ for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) >>> 0;
+ return TASK_TYPE_COLORS[h % TASK_TYPE_COLORS.length];
}