Files
familienarchiv/frontend/src/lib/utils/personFormat.ts
Marcel 8be876492c refactor(date): consolidate formatDate in date.ts with optional format param
Add format?: 'short'|'long' (default 'long') to date.ts formatDate and
remove the duplicate from personFormat.ts. Update DocumentTopBar to
import from date.ts directly. Move the formatDate tests from
personFormat.spec to date.spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-15 13:10:44 +02:00

97 lines
2.7 KiB
TypeScript

import { formatDocumentStatus } from './documentStatusLabel';
import { formatDate } from './date';
type Person = { firstName?: string | null; lastName: string; displayName: string };
type DocumentStatus = 'PLACEHOLDER' | 'UPLOADED' | 'TRANSCRIBED' | 'REVIEWED' | 'ARCHIVED';
type DocForMeta = {
sender?: Person | null;
receivers?: Person[];
documentDate?: string | null;
};
const AVATAR_PALETTE = ['#012851', '#5A3080', '#007596', '#2A6040', '#803020'] as const;
function djb2(str: string): number {
let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = (hash * 33) ^ str.charCodeAt(i);
}
return Math.abs(hash);
}
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 {
if (!person.firstName) return person.lastName;
const first = person.firstName.trim();
const last = person.lastName.trim();
if (!last) return first;
return `${first.charAt(0)}. ${last}`;
}
function abbreviateCompact(person: Person): string {
if (!person.firstName) return person.lastName;
const first = person.firstName.trim();
const last = person.lastName.trim();
if (!last) return first;
return `${first.charAt(0)}.${last}`;
}
export function formatXsMeta(doc: DocForMeta): string {
const parts: string[] = [];
const receivers = doc.receivers ?? [];
if (doc.sender) {
const senderAbbr = abbreviateCompact(doc.sender);
if (receivers.length === 0) {
parts.push(senderAbbr);
} else {
const extra = receivers.length - 1;
const firstReceiver = abbreviateCompact(receivers[0]);
parts.push(
extra > 0
? `${senderAbbr}${firstReceiver} +${extra}`
: `${senderAbbr}${firstReceiver}`
);
}
} else if (receivers.length > 0) {
const extra = receivers.length - 1;
const firstReceiver = abbreviateCompact(receivers[0]);
parts.push(extra > 0 ? `${firstReceiver} +${extra}` : firstReceiver);
}
if (doc.documentDate) {
parts.push(formatDate(doc.documentDate, 'short'));
}
return parts.join(' · ');
}
export function personAvatarColor(personId: string): string {
return AVATAR_PALETTE[djb2(personId) % AVATAR_PALETTE.length];
}
export function statusDotClass(status: DocumentStatus): string {
switch (status) {
case 'PLACEHOLDER':
return 'bg-gray-400';
case 'UPLOADED':
return 'bg-emerald-500';
case 'TRANSCRIBED':
return 'bg-blue-400';
case 'REVIEWED':
return 'bg-amber-400';
case 'ARCHIVED':
return 'bg-emerald-600';
}
}
export function statusLabel(status: string): string {
return formatDocumentStatus(status);
}