refactor(personFormat): replace getInitials(Person) with getInitials(name: string)

Unify the initials-extraction logic: the new string-based getInitials()
splits on whitespace, takes the first char of the first and last word
uppercased — matching the pattern that was already inlined in
CommentThread. Update PersonChip, DocumentMetadataDrawer, and
CommentThread to use the shared function.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-15 13:07:23 +02:00
parent 655a2003cb
commit 76d6f234b4
5 changed files with 34 additions and 18 deletions

View File

@@ -5,6 +5,7 @@ import type { Comment } from '$lib/types';
import MentionEditor from '$lib/components/MentionEditor.svelte';
import { renderBody, extractContent } from '$lib/utils/mention';
import { relativeTime } from '$lib/utils/time';
import { getInitials } from '$lib/utils/personFormat';
import type { MentionDTO } from '$lib/types';
type Props = {
@@ -76,14 +77,6 @@ function isOwn(c: { authorId: string | null }): boolean {
return currentUserId !== null && c.authorId === currentUserId;
}
function getInitials(name: string): string {
return name
.split(/\s+/)
.slice(0, 2)
.map((w) => w.charAt(0).toUpperCase())
.join('');
}
function extractQuote(content: string): { quote: string | null; body: string } {
const match = content.match(/^>\s*"(.+?)"\s*\n\n?([\s\S]*)$/);
if (match) return { quote: match[1], body: match[2] };

View File

@@ -2,7 +2,7 @@
import { m } from '$lib/paraglide/messages.js';
import { formatDate } from '$lib/utils/date';
import { formatDocumentStatus } from '$lib/utils/documentStatusLabel';
import { getInitials as calcInitials, personAvatarColor } from '$lib/utils/personFormat';
import { getInitials, personAvatarColor } from '$lib/utils/personFormat';
type Person = { id: string; firstName?: string | null; lastName: string; displayName: string };
type Tag = { id: string; name: string };
@@ -32,10 +32,6 @@ let showAllReceivers = $state(false);
const displayedReceivers = $derived(showAllReceivers ? receivers : visibleReceivers);
function getInitials(person: Person): string {
return calcInitials(person);
}
function getFullName(person: Person): string {
return person.displayName;
}
@@ -51,7 +47,7 @@ function getFullName(person: Person): string {
style="background-color: {personAvatarColor(person.id)}"
aria-hidden="true"
>
{getInitials(person)}
{getInitials(person.displayName)}
</span>
<span class="font-serif text-sm text-ink">{getFullName(person)}</span>
</a>

View File

@@ -12,7 +12,7 @@ let { person, abbreviated }: Props = $props();
const name = $derived(abbreviated ? abbreviateName(person) : person.displayName);
const avatarColor = $derived(personAvatarColor(person.id));
const initials = $derived(getInitials(person));
const initials = $derived(getInitials(person.displayName));
</script>
<a

View File

@@ -1,5 +1,6 @@
import { describe, it, expect } from 'vitest';
import {
getInitials,
abbreviateName,
formatXsMeta,
personAvatarColor,
@@ -8,6 +9,30 @@ import {
statusLabel
} from './personFormat';
// ─── getInitials ─────────────────────────────────────────────────────────────
describe('getInitials', () => {
it('returns first chars of first and last word uppercased', () => {
expect(getInitials('Marcel Raddatz')).toBe('MR');
});
it('returns single char for a single-word name', () => {
expect(getInitials('Raddatz')).toBe('R');
});
it('returns empty string for an empty name', () => {
expect(getInitials('')).toBe('');
});
it('splits on whitespace only — hyphenated first word counts as one', () => {
expect(getInitials('Anna-Maria Raddatz')).toBe('AR');
});
it('ignores extra whitespace between words', () => {
expect(getInitials(' Karl Raddatz ')).toBe('KR');
});
});
// ─── abbreviateName ──────────────────────────────────────────────────────────
describe('abbreviateName', () => {

View File

@@ -18,9 +18,11 @@ function djb2(str: string): number {
return Math.abs(hash);
}
export function getInitials(person: Person): string {
if (person.firstName) return `${person.firstName[0]}${person.lastName[0]}`.toUpperCase();
return person.lastName.substring(0, 2).toUpperCase();
export function getInitials(name: string): string {
const words = name.trim().split(/\s+/).filter(Boolean);
if (words.length === 0) return '';
if (words.length === 1) return words[0].charAt(0).toUpperCase();
return (words[0].charAt(0) + words[words.length - 1].charAt(0)).toUpperCase();
}
export function abbreviateName(person: Person): string {