feat(web): add lib/phone.ts with formatPhone/normalizePhone/zaloHref helpers
- Create apps/web/lib/phone.ts with VN_PHONE_REGEX, normalizePhone, formatPhone, and zaloHref helpers - Deduplicate phone regex: auth.ts and inquiry.ts now import VN_PHONE_REGEX from @/lib/phone instead of defining their own local patterns - Replace raw .replace(/^0/, '84') in inquiry-detail-dialog.tsx and lead-detail-dialog.tsx with zaloHref(); use formatPhone() for display Resolves GOO-209 Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
} from '@/components/ui/dialog';
|
||||
import { useMarkInquiryRead } from '@/lib/hooks/use-inquiries';
|
||||
import type { InquiryReadDto } from '@/lib/inquiries-api';
|
||||
import { formatPhone, zaloHref } from '@/lib/phone';
|
||||
|
||||
interface InquiryDetailDialogProps {
|
||||
inquiry: InquiryReadDto | null;
|
||||
@@ -42,6 +43,8 @@ export function InquiryDetailDialog({ inquiry, open, onOpenChange }: InquiryDeta
|
||||
minute: '2-digit',
|
||||
});
|
||||
|
||||
const phone = inquiry.phone ?? inquiry.userPhone;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-md sm:max-w-lg">
|
||||
@@ -60,7 +63,7 @@ export function InquiryDetailDialog({ inquiry, open, onOpenChange }: InquiryDeta
|
||||
<InquiryStatusBadge isRead={inquiry.isRead} />
|
||||
</div>
|
||||
<div className="space-y-1 text-sm text-muted-foreground">
|
||||
<p>SĐT: {inquiry.phone ?? inquiry.userPhone}</p>
|
||||
<p>SĐT: {formatPhone(phone)}</p>
|
||||
<p>Ngày gửi: {formattedDate}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -78,13 +81,13 @@ export function InquiryDetailDialog({ inquiry, open, onOpenChange }: InquiryDeta
|
||||
<h4 className="text-sm font-medium">Liên hệ nhanh</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<a
|
||||
href={`tel:${inquiry.phone ?? inquiry.userPhone}`}
|
||||
href={`tel:${phone}`}
|
||||
className="inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm transition-colors hover:bg-accent"
|
||||
>
|
||||
<Phone className="h-4 w-4" aria-hidden="true" /> Gọi điện
|
||||
</a>
|
||||
<a
|
||||
href={`https://zalo.me/${(inquiry.phone ?? inquiry.userPhone).replace(/^0/, '84')}`}
|
||||
href={zaloHref(phone)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm transition-colors hover:bg-accent"
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { Select } from '@/components/ui/select';
|
||||
import { useDeleteLead, useUpdateLeadStatus } from '@/lib/hooks/use-leads';
|
||||
import { LEAD_STATUSES, LEAD_SOURCES, type LeadReadDto, type LeadStatus } from '@/lib/leads-api';
|
||||
import { formatPhone, zaloHref } from '@/lib/phone';
|
||||
|
||||
interface LeadDetailDialogProps {
|
||||
lead: LeadReadDto | null;
|
||||
@@ -96,7 +97,7 @@ export function LeadDetailDialog({ lead, open, onOpenChange }: LeadDetailDialogP
|
||||
<LeadStatusBadge status={lead.status} />
|
||||
</div>
|
||||
<div className="space-y-1 text-sm text-muted-foreground">
|
||||
<p>SĐT: {lead.phone}</p>
|
||||
<p>SĐT: {formatPhone(lead.phone)}</p>
|
||||
{lead.email && <p>Email: {lead.email}</p>}
|
||||
<p>Nguồn: {getSourceLabel(lead.source)}</p>
|
||||
{lead.score !== null && <p>Điểm: {lead.score}/100</p>}
|
||||
@@ -163,7 +164,7 @@ export function LeadDetailDialog({ lead, open, onOpenChange }: LeadDetailDialogP
|
||||
</a>
|
||||
)}
|
||||
<a
|
||||
href={`https://zalo.me/${lead.phone.replace(/^0/, '84')}`}
|
||||
href={zaloHref(lead.phone)}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="inline-flex items-center gap-1.5 rounded-md border px-3 py-1.5 text-sm transition-colors hover:bg-accent"
|
||||
|
||||
Reference in New Issue
Block a user