import React, { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { CheckBadgeIcon, TrashIcon, PlusIcon } from '@heroicons/react/24/outline'; import { api } from '../lib/api'; import type { ApiResponse } from '../lib/api'; import PersianDatePicker from '../components/ui/PersianDatePicker'; import { toEnglishDigits, digitsOnly } from '../lib/utils'; // تبدیل تاریخ میلادی ISO (YYYY-MM-DD) به شمسی Y/m/d برای استعلام api.ir function toJalali(iso: string): string { if (!iso) return ''; const parts = new Intl.DateTimeFormat('en-US-u-ca-persian-nu-latn', { year: 'numeric', month: '2-digit', day: '2-digit', }).formatToParts(new Date(iso + 'T00:00:00')); const get = (t: string) => parts.find(p => p.type === t)?.value ?? ''; const y = get('year'), m = get('month'), d = get('day'); return y && m && d ? `${y}/${m}/${d}` : ''; } // اعتبارسنجی کد ملی ایران (طول ۱۰ + رقم کنترلی) function isValidIranNationalCode(code: string): boolean { if (!/^\d{10}$/.test(code)) return false; if (/^(\d)\1{9}$/.test(code)) return false; // ارقام یکسان نامعتبر const check = +code[9]; let sum = 0; for (let i = 0; i < 9; i++) sum += +code[i] * (10 - i); const r = sum % 11; return r < 2 ? check === r : check === 11 - r; } // اعتبارسنجی شبای ایران: IR + ۲۴ رقم + کنترل mod-97 function isValidIranIban(raw: string): boolean { const iban = toEnglishDigits(raw).replace(/\s/g, '').toUpperCase(); if (!/^IR\d{24}$/.test(iban)) return false; const rearranged = iban.slice(4) + iban.slice(0, 4); const numeric = rearranged.replace(/[A-Z]/g, (c) => String(c.charCodeAt(0) - 55)); let rem = 0; for (const ch of numeric) rem = (rem * 10 + +ch) % 97; return rem === 1; } interface IbanItem { id: string; iban: string; bank_name: string | null; owner_name: string | null; verified: boolean; } interface RepProfile { full_name: string; mobile_number: string | null; national_code: string | null; national_code_verified: boolean; bank_account: IbanItem[] | null; } // پاسخ تک‌منبعی ممکن است double-nested باشد // eslint-disable-next-line @typescript-eslint/no-explicit-any const unwrap = (d: any): RepProfile | undefined => d?.data?.data ?? d?.data; export default function RepresentationProfilePage() { const qc = useQueryClient(); const [nationalCode, setNationalCode] = useState(''); const [iban, setIban] = useState(''); const [birthDate, setBirthDate] = useState(''); const meQ = useQuery({ queryKey: ['representation-me'], queryFn: () => api.get>('/api/v1/representation/me'), staleTime: 30_000, }); const profile = unwrap(meQ.data); const verified = profile?.national_code_verified ?? false; const ibans = profile?.bank_account ?? []; const verifyMut = useMutation({ mutationFn: (code: string) => api.post>('/api/v1/representation/verify-national-code', { national_code: code }), onSuccess: () => { toast.success('کد ملی با موفقیت تأیید شد'); setNationalCode(''); qc.invalidateQueries({ queryKey: ['representation-me'] }); }, onError: (e: Error) => toast.error(e.message), }); const addIbanMut = useMutation({ mutationFn: (payload: { iban: string; birth_date: string }) => api.post>('/api/v1/representation/iban', payload), onSuccess: () => { toast.success('شماره شبا تأیید و اضافه شد'); setIban(''); setBirthDate(''); qc.invalidateQueries({ queryKey: ['representation-me'] }); }, onError: (e: Error) => toast.error(e.message), }); const removeIbanMut = useMutation({ mutationFn: (id: string) => api.delete>(`/api/v1/representation/iban/${id}`), onSuccess: () => { toast.success('شماره شبا حذف شد'); qc.invalidateQueries({ queryKey: ['representation-me'] }); }, onError: (e: Error) => toast.error(e.message), }); const submitNationalCode = () => { const code = digitsOnly(nationalCode); if (!isValidIranNationalCode(code)) { toast.error('کد ملی نامعتبر است'); return; } verifyMut.mutate(code); }; const submitIban = () => { const clean = toEnglishDigits(iban).replace(/\s/g, '').toUpperCase(); if (!isValidIranIban(clean)) { toast.error('شماره شبا نامعتبر است (IR + ۲۴ رقم)'); return; } const jalali = toJalali(birthDate); if (!jalali) { toast.error('تاریخ تولد را انتخاب کنید'); return; } addIbanMut.mutate({ iban: clean, birth_date: jalali }); }; return (

پروفایل نماینده

تأیید هویت و مدیریت شماره‌های شبا
{/* تأیید کد ملی */}

تأیید هویت

{verified && ( کد ملی تأیید شده )}
{verified ? (
کد ملی: {profile?.national_code}
) : (
setNationalCode(digitsOnly(e.target.value, 10))} style={{ width: 220, height: 38, padding: '0 12px', borderRadius: 'var(--r-sm)', border: '1px solid var(--border)', background: 'var(--surface)', color: 'var(--text)', fontSize: 13.5, boxSizing: 'border-box', textAlign: 'center' }} /> با استعلام شاهکار بررسی می‌شود
)}
{/* شماره‌های شبا */}

شماره‌های شبا

{ibans.length} از ۲
{ibans.length === 0 && ( )} {ibans.map((b) => ( ))}
شماره شبابانکصاحب حسابوضعیت
هنوز شبایی ثبت نشده است
{b.iban} {b.bank_name ?? '—'} {b.owner_name ?? '—'} {b.verified ? 'تأیید شده' : 'تأیید نشده'}
{!verified && (
برای افزودن شبا ابتدا باید کد ملی خود را تأیید کنید.
)} {verified && ibans.length < 2 && (
setIban(toEnglishDigits(e.target.value).toUpperCase().replace(/[^IR0-9]/g, ''))} style={{ width: 320, height: 38, padding: '0 12px', borderRadius: 'var(--r-sm)', border: '1px solid var(--border)', background: 'var(--surface)', color: 'var(--text)', fontSize: 13, boxSizing: 'border-box', fontFamily: 'monospace' }} /> تاریخ تولد فقط برای استعلام مالکیت شبا استفاده می‌شود و ذخیره نمی‌شود.
)}
); }