Add shared form fields for contact details
This commit is contained in:
37
app/utils/formatters/email.ts
Normal file
37
app/utils/formatters/email.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Basic helpers around email formatting shared across components and composables.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Normalises an email by trimming whitespace and converting it to lowercase.
|
||||
*/
|
||||
export const normalizeEmail = (rawValue: string): string => {
|
||||
const value = (rawValue || '').trim()
|
||||
return value.toLowerCase()
|
||||
}
|
||||
|
||||
/**
|
||||
* Masks an email address by hiding the characters between the first and last letters
|
||||
* of the local part. Useful for UI fragments where we want partial obfuscation.
|
||||
*/
|
||||
export const maskEmail = (rawValue: string): string => {
|
||||
const value = normalizeEmail(rawValue)
|
||||
if (!value) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const [localPart, domain] = value.split('@')
|
||||
if (!domain) {
|
||||
return value
|
||||
}
|
||||
|
||||
if (localPart.length <= 2) {
|
||||
return `${localPart[0] ?? ''}·@${domain}`
|
||||
}
|
||||
|
||||
const start = localPart[0]
|
||||
const end = localPart.slice(-1)
|
||||
const masked = '·'.repeat(Math.max(0, localPart.length - 2))
|
||||
|
||||
return `${start}${masked}${end}@${domain}`
|
||||
}
|
||||
67
app/utils/formatters/phone.ts
Normal file
67
app/utils/formatters/phone.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Utilities to normalize and format phone numbers without relying on external libraries.
|
||||
* The helpers keep the behaviour permissive to avoid breaking existing flows while
|
||||
* still providing a single place where formatting rules live.
|
||||
*/
|
||||
|
||||
/** Matches characters that should be kept when normalising a phone number. */
|
||||
const PHONE_CHAR_PATTERN = /[^+\d]/g
|
||||
|
||||
/**
|
||||
* Normalises a phone number by trimming whitespace, removing spacing/separators and
|
||||
* converting international prefixes written with `00` to their `+` variant.
|
||||
*/
|
||||
export const normalizePhone = (rawValue: string): string => {
|
||||
const trimmed = (rawValue || '').trim()
|
||||
if (!trimmed) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const cleaned = trimmed.replace(PHONE_CHAR_PATTERN, '')
|
||||
if (cleaned.startsWith('00')) {
|
||||
return `+${cleaned.slice(2)}`
|
||||
}
|
||||
|
||||
return cleaned
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a phone number by grouping digits by two while keeping any international
|
||||
* prefix. The function remains tolerant to partially entered numbers.
|
||||
*/
|
||||
export const formatPhone = (rawValue: string): string => {
|
||||
const normalized = normalizePhone(rawValue)
|
||||
if (!normalized) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const hasInternationalPrefix = normalized.startsWith('+')
|
||||
const prefix = hasInternationalPrefix ? normalized.slice(0, 1) : ''
|
||||
const digits = hasInternationalPrefix ? normalized.slice(1) : normalized
|
||||
|
||||
const groups = digits.match(/.{1,2}/g) ?? []
|
||||
const grouped = groups.join(' ')
|
||||
|
||||
return prefix ? `${prefix}${grouped ? ' ' : ''}${grouped}` : grouped
|
||||
}
|
||||
|
||||
/**
|
||||
* Masks a phone number for display purposes by replacing the middle digits with ·.
|
||||
* Useful for UI fragments where the full number should not be exposed.
|
||||
*/
|
||||
export const maskPhone = (rawValue: string): string => {
|
||||
const normalized = normalizePhone(rawValue)
|
||||
if (!normalized) {
|
||||
return ''
|
||||
}
|
||||
|
||||
if (normalized.length <= 4) {
|
||||
return normalized
|
||||
}
|
||||
|
||||
const start = normalized.slice(0, 2)
|
||||
const end = normalized.slice(-2)
|
||||
const maskedMiddle = '·'.repeat(Math.max(0, normalized.length - 4))
|
||||
|
||||
return `${start}${maskedMiddle}${end}`
|
||||
}
|
||||
Reference in New Issue
Block a user