83 lines
2.6 KiB
TypeScript
83 lines
2.6 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { Eye, EyeOff, Copy, Check } from 'lucide-react';
|
|
import { useToast } from './Toast';
|
|
|
|
export interface ObfuscatedIdNumberProps {
|
|
idNumber?: string | null;
|
|
className?: string;
|
|
allowToggle?: boolean;
|
|
}
|
|
|
|
export function maskIdNumber(id: string): string {
|
|
const clean = id ? id.trim() : '';
|
|
if (clean.length <= 4) {
|
|
return '••••';
|
|
}
|
|
if (clean.length <= 8) {
|
|
return `${clean.slice(0, 2)}••••${clean.slice(-2)}`;
|
|
}
|
|
// SA ID (13 digits) or standard national IDs: keep first 6 (DOB), mask middle 5, show last 2
|
|
return `${clean.slice(0, 6)}•••••${clean.slice(-2)}`;
|
|
}
|
|
|
|
export function ObfuscatedIdNumber({
|
|
idNumber,
|
|
className = '',
|
|
allowToggle = true,
|
|
}: ObfuscatedIdNumberProps): React.ReactElement {
|
|
const [isRevealed, setIsRevealed] = useState<boolean>(false);
|
|
const [copied, setCopied] = useState<boolean>(false);
|
|
const { success } = useToast();
|
|
|
|
if (!idNumber) {
|
|
return <span className="text-slate-600 font-mono text-xs">-</span>;
|
|
}
|
|
|
|
const rawId: string = idNumber;
|
|
|
|
const handleCopy = async (e: React.MouseEvent<HTMLButtonElement>): Promise<void> => {
|
|
e.stopPropagation();
|
|
try {
|
|
await navigator.clipboard.writeText(rawId);
|
|
setCopied(true);
|
|
success('Copied to Clipboard', 'ID Number copied.');
|
|
setTimeout(() => setCopied(false), 2000);
|
|
} catch {
|
|
// Clipboard fallback
|
|
}
|
|
};
|
|
|
|
const handleToggle = (e: React.MouseEvent<HTMLButtonElement>): void => {
|
|
e.stopPropagation();
|
|
setIsRevealed((prev) => !prev);
|
|
};
|
|
|
|
const displayedText = isRevealed ? rawId : maskIdNumber(rawId);
|
|
|
|
return (
|
|
<div className={`inline-flex items-center gap-1.5 font-mono text-xs text-slate-300 ${className}`}>
|
|
<span className="tracking-wider select-all">{displayedText}</span>
|
|
|
|
{allowToggle && (
|
|
<button
|
|
type="button"
|
|
onClick={handleToggle}
|
|
title={isRevealed ? 'Hide ID Number' : 'Reveal ID Number'}
|
|
className="p-1 rounded hover:bg-slate-800 text-slate-400 hover:text-slate-200 transition-colors cursor-pointer"
|
|
>
|
|
{isRevealed ? <EyeOff className="w-3.5 h-3.5 text-blue-400" /> : <Eye className="w-3.5 h-3.5" />}
|
|
</button>
|
|
)}
|
|
|
|
<button
|
|
type="button"
|
|
onClick={handleCopy}
|
|
title="Copy ID Number"
|
|
className="p-1 rounded hover:bg-slate-800 text-slate-400 hover:text-slate-200 transition-colors cursor-pointer"
|
|
>
|
|
{copied ? <Check className="w-3.5 h-3.5 text-emerald-400" /> : <Copy className="w-3.5 h-3.5" />}
|
|
</button>
|
|
</div>
|
|
);
|
|
}
|