feature: phase0 - Global patients + mobile normalization
This commit is contained in:
52
backend/src/common/phone.spec.ts
Normal file
52
backend/src/common/phone.spec.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import {
|
||||
formatMobileForDisplay,
|
||||
isValidMobile,
|
||||
mobileSearchDigits,
|
||||
normalizeMobile,
|
||||
} from './phone';
|
||||
|
||||
describe('normalizeMobile', () => {
|
||||
it('normalizes 09-prefixed numbers', () => {
|
||||
expect(normalizeMobile('09121234567')).toBe('+989121234567');
|
||||
});
|
||||
|
||||
it('normalizes without leading zero', () => {
|
||||
expect(normalizeMobile('9121234567')).toBe('+989121234567');
|
||||
});
|
||||
|
||||
it('normalizes +98 prefix', () => {
|
||||
expect(normalizeMobile('+989121234567')).toBe('+989121234567');
|
||||
});
|
||||
|
||||
it('normalizes 0098 prefix', () => {
|
||||
expect(normalizeMobile('00989121234567')).toBe('+989121234567');
|
||||
});
|
||||
|
||||
it('normalizes spaced input', () => {
|
||||
expect(normalizeMobile('0912 123 4567')).toBe('+989121234567');
|
||||
});
|
||||
|
||||
it('rejects invalid numbers', () => {
|
||||
expect(normalizeMobile('123')).toBeNull();
|
||||
expect(normalizeMobile('')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValidMobile', () => {
|
||||
it('validates normalized mobile', () => {
|
||||
expect(isValidMobile('+989121234567')).toBe(true);
|
||||
expect(isValidMobile('09121234567')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatMobileForDisplay', () => {
|
||||
it('formats E.164 to local spaced form', () => {
|
||||
expect(formatMobileForDisplay('+989121234567')).toBe('0912 123 4567');
|
||||
});
|
||||
});
|
||||
|
||||
describe('mobileSearchDigits', () => {
|
||||
it('strips non-digits', () => {
|
||||
expect(mobileSearchDigits('+98 912-123-4567')).toBe('989121234567');
|
||||
});
|
||||
});
|
||||
54
backend/src/common/phone.ts
Normal file
54
backend/src/common/phone.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/** Canonical Iran mobile: +989XXXXXXXXX (12 chars). */
|
||||
export const IR_MOBILE_REGEX = /^\+989\d{9}$/;
|
||||
|
||||
/**
|
||||
* Normalize user-entered mobile to E.164 for Iran (+98…).
|
||||
* Accepts 09…, 9…, +98…, 0098… with optional spaces/dashes.
|
||||
*/
|
||||
export function normalizeMobile(input: string): string | null {
|
||||
const trimmed = input?.trim();
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let digits = trimmed.replace(/[^\d+]/g, '');
|
||||
if (digits.startsWith('+')) {
|
||||
digits = digits.slice(1);
|
||||
}
|
||||
|
||||
digits = digits.replace(/\D/g, '');
|
||||
|
||||
if (digits.startsWith('0098')) {
|
||||
digits = digits.slice(4);
|
||||
} else if (digits.startsWith('98') && digits.length >= 12) {
|
||||
digits = digits.slice(2);
|
||||
}
|
||||
|
||||
if (digits.startsWith('0') && digits.length === 11) {
|
||||
digits = digits.slice(1);
|
||||
}
|
||||
|
||||
if (digits.length === 10 && digits.startsWith('9')) {
|
||||
return `+98${digits}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isValidMobile(normalized: string): boolean {
|
||||
return IR_MOBILE_REGEX.test(normalized);
|
||||
}
|
||||
|
||||
/** Display-friendly local format: 09XX XXX XXXX */
|
||||
export function formatMobileForDisplay(normalized: string): string {
|
||||
if (!isValidMobile(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
const local = `0${normalized.slice(3)}`;
|
||||
return `${local.slice(0, 4)} ${local.slice(4, 7)} ${local.slice(7)}`;
|
||||
}
|
||||
|
||||
/** Strip to digits only for partial search matching. */
|
||||
export function mobileSearchDigits(input: string): string {
|
||||
return input.replace(/\D/g, '');
|
||||
}
|
||||
Reference in New Issue
Block a user