Files
cbsnexora/src/app/clients/utils/name-case.util.ts
T

79 lines
3.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
* Casing for client names: the name is stored exactly as the officer typed it, except that a word
* starting with a lower-case letter has that first letter raised. Nothing else is touched.
*
* 'keshav Singh sisodiya' → 'Keshav Singh Sisodiya'
* 'lincpay lTD' → 'Lincpay LTD'
* 'Technoludium Solutions Private Limited' → unchanged
* 'TECHNOLUDIUM SOLUTIONS' → unchanged
*
* Deliberately NOT full title-casing. Lower-casing the rest of each word would rewrite an entity
* name the officer entered as it appears on the certificate — 'TECHNOLUDIUM' becoming
* 'Technoludium', 'Ltd' becoming a forced 'LTD'. That name is matched character-for-character
* against the PAN holder's registered name during KYC verification, so the typed spelling is the
* thing to preserve; only an unintended lower-case first letter is corrected.
*/
/** Characters after which a new word starts: 'jean-marie' → 'Jean-Marie'. */
const SEGMENT_STARTERS = new Set([
'-',
"'",
'’'
]);
/**
* Raises the first letter of each word in a single space-delimited token, leaving every other
* character as typed.
*/
function capitalizeWord(word: string): string {
let result = '';
for (let i = 0; i < word.length; i++) {
const previous = i === 0 ? '' : word[i - 1];
let startsWord = i === 0 || SEGMENT_STARTERS.has(previous);
if (previous === "'" || previous === '’') {
// 'o'brien' / 'd'souza' capitalise after the apostrophe, but a possessive
// ('lincpay's') must not — only a single leading letter marks the name prefix.
startsWord = i === 2;
}
result += startsWord ? word[i].toUpperCase() : word[i];
}
return result;
}
/**
* Applies the casing rule above to a person or entity name.
* Trims and collapses runs of whitespace; non-string input yields ''.
*/
export function toProperNameCase(value: string | null | undefined): string {
if (typeof value !== 'string') {
return '';
}
const collapsed = value.trim().replace(/\s+/g, ' ');
if (!collapsed) {
return '';
}
return collapsed.split(' ').map(capitalizeWord).join(' ');
}
/**
* Title case for an individual's first, middle and last name: each word's first letter raised,
* the rest lowered, so 'SIngh' and 'singh' both become 'Singh'. This is what the Full Name line on
* the general step has always shown; using it for the stored value too keeps the preview and the
* saved client the same as that line. Entity names stay on {@link toProperNameCase} — their
* spelling is matched against the registered PAN name.
*/
export function toPersonNameCase(value: string | null | undefined): string {
if (typeof value !== 'string') {
return '';
}
return toProperNameCase(value.toLowerCase());
}