Implement comprehensive dark/light mode overhaul for admin panel
- Refactor color palette in `ui-design-spec.md` to utilize CSS variables exclusively, eliminating fixed hex values and Tailwind utility classes. - Complete dark mode implementation in `uiStore.ts`, ensuring proper theme application via `applyTheme()` and `applyBrand()`. - Create `admin-theme-dark-light-audit.md` to document the transition process, outlining issues with inline styles and fixed colors. - Introduce `theme-tokens.test.ts` to enforce rules against fixed hex colors and ensure compliance with the design system. - Update various components and styles to replace inline styles and fixed colors with CSS variables, ensuring consistent theming across light and dark modes. - Ensure all changes maintain visual integrity in both light and dark modes, with a focus on accessibility and contrast standards.
This commit is contained in:
@@ -72,10 +72,10 @@ const MEDICAL_SECTIONS: {
|
||||
|
||||
const ROLE_META: Record<string, { label: string; cls: string; dot: string }> = {
|
||||
admin: { label: 'ادمین', cls: 'bg-violet-100 dark:bg-violet-400/10 text-violet-700 dark:text-violet-300', dot: '#8b5cf6' },
|
||||
doctor: { label: 'پزشک', cls: 'bg-blue-100 dark:bg-blue-400/10 text-blue-700 dark:text-blue-300', dot: '#3b82f6' },
|
||||
doctor: { label: 'پزشک', cls: 'bg-[var(--info-bg)] dark:bg-blue-400/10 text-[var(--info)] dark:text-blue-300', dot: '#3b82f6' },
|
||||
secretary: { label: 'منشی', cls: 'bg-orange-100 dark:bg-orange-400/10 text-orange-700 dark:text-orange-300', dot: '#f97316' },
|
||||
clinic: { label: 'کلینیک', cls: 'bg-emerald-100 dark:bg-emerald-400/10 text-emerald-700 dark:text-emerald-300', dot: '#10b981' },
|
||||
patient: { label: 'بیمار', cls: 'bg-slate-100 dark:bg-slate-400/10 text-slate-600 dark:text-slate-400', dot: '#94a3b8' },
|
||||
patient: { label: 'بیمار', cls: 'bg-[var(--surface-2)] dark:bg-slate-400/10 text-[var(--text-2)] dark:text-[var(--text-3)]', dot: '#94a3b8' },
|
||||
};
|
||||
|
||||
const ROLE_MAP: Record<string, string[]> = {
|
||||
@@ -107,7 +107,7 @@ function BigAvatar({ name, id }: { name: string | null; id: number }) {
|
||||
? name.split(' ').filter(Boolean).map(w => w[0]).join('').toUpperCase().slice(0, 2)
|
||||
: '؟';
|
||||
return (
|
||||
<div className={`w-20 h-20 rounded-2xl bg-gradient-to-br ${AVATAR_COLORS[id % AVATAR_COLORS.length]} flex items-center justify-center text-white text-2xl font-bold shadow-lg shrink-0`}>
|
||||
<div className={`w-20 h-20 rounded-2xl bg-gradient-to-br ${AVATAR_COLORS[id % AVATAR_COLORS.length]} flex items-center justify-center text-[var(--on-primary)] text-2xl font-bold shadow-lg shrink-0`}>
|
||||
{initials}
|
||||
</div>
|
||||
);
|
||||
@@ -124,21 +124,21 @@ function InfoCard({ icon: Icon, label, value, mono = false, copyable = false }:
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="flex items-start gap-3 p-4 rounded-xl bg-slate-50 dark:bg-gray-800/60 hover:bg-slate-100 dark:hover:bg-gray-800 transition-colors group">
|
||||
<div className="w-9 h-9 rounded-lg bg-white dark:bg-gray-700 shadow-sm flex items-center justify-center shrink-0">
|
||||
<Icon className="w-4.5 h-4.5 text-slate-500 dark:text-slate-400" />
|
||||
<div className="flex items-start gap-3 p-4 rounded-xl bg-[var(--surface-2)] dark:bg-[var(--surface)]/60 hover:bg-[var(--surface-2)] dark:hover:bg-[var(--surface)] transition-colors group">
|
||||
<div className="w-9 h-9 rounded-lg bg-[var(--surface)] shadow-sm flex items-center justify-center shrink-0">
|
||||
<Icon className="w-4.5 h-4.5 text-[var(--text-2)]" />
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-[11px] font-medium text-slate-400 dark:text-slate-500 uppercase tracking-wide mb-0.5">{label}</p>
|
||||
<p className={`text-sm font-medium text-slate-800 dark:text-slate-200 truncate ${mono ? 'font-mono' : ''}`}
|
||||
<p className="text-[11px] font-medium text-[var(--text-3)] uppercase tracking-wide mb-0.5">{label}</p>
|
||||
<p className={`text-sm font-medium text-[var(--text)] dark:text-[var(--text)] truncate ${mono ? 'font-mono' : ''}`}
|
||||
dir={mono ? 'ltr' : undefined}>
|
||||
{value ?? <span className="text-slate-400 dark:text-slate-500 font-normal">ثبت نشده</span>}
|
||||
{value ?? <span className="text-[var(--text-3)] font-normal">ثبت نشده</span>}
|
||||
</p>
|
||||
</div>
|
||||
{copyable && typeof value === 'string' && (
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="opacity-0 group-hover:opacity-100 w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 hover:bg-white dark:hover:bg-gray-700 transition-all shrink-0"
|
||||
className="opacity-0 group-hover:opacity-100 w-7 h-7 flex items-center justify-center rounded-lg text-[var(--text-3)] hover:text-[var(--text-2)] dark:hover:text-[var(--text-3)] hover:bg-[var(--surface)] dark:hover:bg-[var(--surface-2)] transition-all shrink-0"
|
||||
>
|
||||
<ClipboardDocumentIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
@@ -181,13 +181,13 @@ function ChangeRoleModal({ current, loading, onSave, onClose }: {
|
||||
className={`flex items-center gap-3 p-3 rounded-xl cursor-pointer border-2 transition-all ${
|
||||
selected === key
|
||||
? 'border-primary-500 bg-primary-50 dark:bg-primary-500/10'
|
||||
: 'border-transparent hover:bg-slate-50 dark:hover:bg-gray-800 border border-slate-200 dark:border-gray-700'
|
||||
: 'border-transparent hover:bg-[var(--surface-2)] dark:hover:bg-[var(--surface)] border border-[var(--border)] dark:border-[var(--border)]'
|
||||
}`}
|
||||
>
|
||||
<input type="radio" name="role" value={key} checked={selected === key}
|
||||
onChange={() => setSelected(key)} className="sr-only" />
|
||||
<span className="w-2.5 h-2.5 rounded-full shrink-0" style={{ backgroundColor: meta.dot }} />
|
||||
<span className="text-sm font-medium text-slate-800 dark:text-slate-200 flex-1">{meta.label}</span>
|
||||
<span className="text-sm font-medium text-[var(--text)] flex-1">{meta.label}</span>
|
||||
{selected === key && <CheckCircleIcon className="w-4 h-4 text-primary-600 dark:text-primary-400" />}
|
||||
</label>
|
||||
))}
|
||||
@@ -320,11 +320,11 @@ export default function UserDetailPage() {
|
||||
return (
|
||||
<div className="animate-slide-up">
|
||||
<div className="cp-card p-16 flex flex-col items-center gap-4 text-center">
|
||||
<div className="w-16 h-16 rounded-2xl bg-red-100 dark:bg-red-400/10 flex items-center justify-center">
|
||||
<XCircleIcon className="w-8 h-8 text-red-500" />
|
||||
<div className="w-16 h-16 rounded-2xl bg-[var(--danger-bg)] dark:bg-red-400/10 flex items-center justify-center">
|
||||
<XCircleIcon className="w-8 h-8 text-[var(--danger)]" />
|
||||
</div>
|
||||
<p className="font-semibold text-slate-800 dark:text-slate-200">کاربر یافت نشد</p>
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400">این کاربر حذف شده یا UUID اشتباه است.</p>
|
||||
<p className="font-semibold text-[var(--text)]">کاربر یافت نشد</p>
|
||||
<p className="text-sm text-[var(--text-2)]">این کاربر حذف شده یا UUID اشتباه است.</p>
|
||||
<button onClick={() => navigate('/admin/users')} className="cp-btn-secondary mt-2">
|
||||
بازگشت به لیست کاربران
|
||||
</button>
|
||||
@@ -340,16 +340,16 @@ export default function UserDetailPage() {
|
||||
<div className="animate-slide-up space-y-5">
|
||||
|
||||
{/* ── Breadcrumb / Back ─────────────────────────────────── */}
|
||||
<div className="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400">
|
||||
<div className="flex items-center gap-2 text-sm text-[var(--text-2)]">
|
||||
<button
|
||||
onClick={() => navigate('/admin/users')}
|
||||
className="flex items-center gap-1.5 hover:text-slate-800 dark:hover:text-slate-100 transition-colors"
|
||||
className="flex items-center gap-1.5 hover:text-[var(--text)] dark:hover:text-[var(--text)] transition-colors"
|
||||
>
|
||||
<ArrowRightIcon className="w-4 h-4" />
|
||||
کاربران
|
||||
</button>
|
||||
<span>/</span>
|
||||
<span className="text-slate-700 dark:text-slate-300 font-medium">پروفایل کاربر</span>
|
||||
<span className="text-[var(--text)] font-medium">پروفایل کاربر</span>
|
||||
</div>
|
||||
|
||||
{/* ── Profile Card ──────────────────────────────────────── */}
|
||||
@@ -363,8 +363,8 @@ export default function UserDetailPage() {
|
||||
<BigAvatar name={user.name} id={user.id} />
|
||||
|
||||
<div className="flex-1 min-w-0 sm:mb-1">
|
||||
<h1 className="text-xl font-bold text-slate-900 dark:text-slate-50 leading-tight">
|
||||
{user.name || <span className="text-slate-400 font-normal">نام ثبت نشده</span>}
|
||||
<h1 className="text-xl font-bold text-[var(--text)] dark:text-slate-50 leading-tight">
|
||||
{user.name || <span className="text-[var(--text-3)] font-normal">نام ثبت نشده</span>}
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 mt-1.5 flex-wrap">
|
||||
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-0.5 rounded-full ${roleMeta.cls}`}>
|
||||
@@ -372,15 +372,15 @@ export default function UserDetailPage() {
|
||||
{roleMeta.label}
|
||||
</span>
|
||||
{user.is_active ? (
|
||||
<span className="inline-flex items-center gap-1 text-xs font-medium px-2.5 py-0.5 rounded-full bg-green-100 dark:bg-green-400/10 text-green-700 dark:text-green-300">
|
||||
<span className="inline-flex items-center gap-1 text-xs font-medium px-2.5 py-0.5 rounded-full bg-[var(--success-bg)] dark:bg-green-400/10 text-[var(--success)] dark:text-green-300">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-green-500 animate-pulse" />فعال
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center gap-1 text-xs font-medium px-2.5 py-0.5 rounded-full bg-slate-100 dark:bg-slate-400/10 text-slate-500 dark:text-slate-400">
|
||||
<span className="inline-flex items-center gap-1 text-xs font-medium px-2.5 py-0.5 rounded-full bg-[var(--surface-2)] dark:bg-slate-400/10 text-[var(--text-2)]">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-slate-400" />غیرفعال
|
||||
</span>
|
||||
)}
|
||||
<span className="text-xs text-slate-400 dark:text-slate-500">#{user.id}</span>
|
||||
<span className="text-xs text-[var(--text-3)]">#{user.id}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -404,29 +404,29 @@ export default function UserDetailPage() {
|
||||
<EllipsisVerticalIcon className="w-4 h-4" />
|
||||
</button>
|
||||
{menuOpen && (
|
||||
<div className="absolute left-0 top-12 z-30 w-52 cp-card shadow-2xl border border-slate-200 dark:border-gray-700 py-1.5 animate-scale-in" data-upmenu>
|
||||
<div className="absolute left-0 top-12 z-30 w-52 cp-card shadow-2xl border border-[var(--border)] py-1.5 animate-scale-in" data-upmenu>
|
||||
<button
|
||||
onClick={() => { toggleStatusMut.mutate(); setMenuOpen(false); }}
|
||||
disabled={toggleStatusMut.isPending}
|
||||
className="w-full flex items-center gap-2.5 px-3 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-gray-800 transition-colors disabled:opacity-50"
|
||||
className="w-full flex items-center gap-2.5 px-3 py-2 text-sm text-[var(--text)] hover:bg-[var(--surface-2)] dark:hover:bg-[var(--surface)] transition-colors disabled:opacity-50"
|
||||
>
|
||||
{user.is_active
|
||||
? <XCircleIcon className="w-4 h-4 text-orange-400 shrink-0" />
|
||||
: <CheckCircleIcon className="w-4 h-4 text-green-500 shrink-0" />
|
||||
: <CheckCircleIcon className="w-4 h-4 text-[var(--success)] shrink-0" />
|
||||
}
|
||||
{user.is_active ? 'غیرفعال کردن حساب' : 'فعالسازی حساب'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => { setRoleOpen(true); setMenuOpen(false); }}
|
||||
className="w-full flex items-center gap-2.5 px-3 py-2 text-sm text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-gray-800 transition-colors"
|
||||
className="w-full flex items-center gap-2.5 px-3 py-2 text-sm text-[var(--text)] hover:bg-[var(--surface-2)] dark:hover:bg-[var(--surface)] transition-colors"
|
||||
>
|
||||
<ShieldCheckIcon className="w-4 h-4 text-violet-400 shrink-0" />
|
||||
تغییر نقش کاربر
|
||||
</button>
|
||||
<div className="border-t border-slate-100 dark:border-gray-700 my-1" />
|
||||
<div className="border-t border-[var(--border)] my-1" />
|
||||
<button
|
||||
onClick={() => { setDeleteOpen(true); setMenuOpen(false); }}
|
||||
className="w-full flex items-center gap-2.5 px-3 py-2 text-sm text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-500/10 transition-colors"
|
||||
className="w-full flex items-center gap-2.5 px-3 py-2 text-sm text-[var(--danger)] dark:text-red-400 hover:bg-[var(--danger-bg)] dark:hover:bg-[var(--danger)]/10 transition-colors"
|
||||
>
|
||||
<TrashIcon className="w-4 h-4 shrink-0" />
|
||||
حذف این کاربر
|
||||
@@ -443,7 +443,7 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* Left: Contact info */}
|
||||
<div className="lg:col-span-2 cp-card p-6 space-y-4">
|
||||
<h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300">اطلاعات تماس و حساب</h2>
|
||||
<h2 className="text-sm font-semibold text-[var(--text)]">اطلاعات تماس و حساب</h2>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<InfoCard icon={UserIcon} label="نام کامل" value={user.name} />
|
||||
<InfoCard icon={PhoneIcon} label="شماره موبایل" value={user.mobile_number} mono copyable />
|
||||
@@ -459,10 +459,10 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* Roles card */}
|
||||
<div className="cp-card p-5 space-y-3">
|
||||
<h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300">نقشهای سیستمی</h2>
|
||||
<h2 className="text-sm font-semibold text-[var(--text)]">نقشهای سیستمی</h2>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{user.roles.map((r) => (
|
||||
<span key={r} className="text-xs font-mono px-2.5 py-1 rounded-lg bg-slate-100 dark:bg-gray-800 text-slate-600 dark:text-slate-400 border border-slate-200 dark:border-gray-700">
|
||||
<span key={r} className="text-xs font-mono px-2.5 py-1 rounded-lg bg-[var(--surface-2)] text-[var(--text-2)] border border-[var(--border)]">
|
||||
{r}
|
||||
</span>
|
||||
))}
|
||||
@@ -477,14 +477,14 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* UUID card */}
|
||||
<div className="cp-card p-5 space-y-2">
|
||||
<h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300">شناسه یکتا (UUID)</h2>
|
||||
<h2 className="text-sm font-semibold text-[var(--text)]">شناسه یکتا (UUID)</h2>
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="text-[11px] font-mono text-slate-500 dark:text-slate-400 break-all flex-1" dir="ltr">
|
||||
<p className="text-[11px] font-mono text-[var(--text-2)] break-all flex-1" dir="ltr">
|
||||
{user.uuid}
|
||||
</p>
|
||||
<button
|
||||
onClick={() => { navigator.clipboard.writeText(user.uuid); toast.success('UUID کپی شد'); }}
|
||||
className="w-7 h-7 flex items-center justify-center rounded-lg text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 hover:bg-slate-100 dark:hover:bg-gray-700 transition-colors shrink-0"
|
||||
className="w-7 h-7 flex items-center justify-center rounded-lg text-[var(--text-3)] hover:text-[var(--text-2)] dark:hover:text-[var(--text-3)] hover:bg-[var(--surface-2)] dark:hover:bg-[var(--surface-2)] transition-colors shrink-0"
|
||||
>
|
||||
<ClipboardDocumentIcon className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
@@ -494,13 +494,13 @@ export default function UserDetailPage() {
|
||||
{/* Status card */}
|
||||
<div className="cp-card p-5">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300">وضعیت حساب</h2>
|
||||
<h2 className="text-sm font-semibold text-[var(--text)]">وضعیت حساب</h2>
|
||||
{user.is_active
|
||||
? <span className="text-xs font-medium text-green-600 dark:text-green-400">فعال</span>
|
||||
: <span className="text-xs font-medium text-slate-500">غیرفعال</span>
|
||||
? <span className="text-xs font-medium text-[var(--success)] dark:text-green-400">فعال</span>
|
||||
: <span className="text-xs font-medium text-[var(--text-2)]">غیرفعال</span>
|
||||
}
|
||||
</div>
|
||||
<div className={`w-full h-2 rounded-full ${user.is_active ? 'bg-green-200 dark:bg-green-400/20' : 'bg-slate-200 dark:bg-gray-700'}`}>
|
||||
<div className={`w-full h-2 rounded-full ${user.is_active ? 'bg-green-200 dark:bg-green-400/20' : 'bg-[var(--surface-3)] dark:bg-[var(--surface-2)]'}`}>
|
||||
<div className={`h-full rounded-full transition-all ${user.is_active ? 'w-full bg-green-500' : 'w-0'}`} />
|
||||
</div>
|
||||
<button
|
||||
@@ -509,7 +509,7 @@ export default function UserDetailPage() {
|
||||
className={`w-full mt-4 text-sm font-medium py-2 rounded-xl transition-colors disabled:opacity-50 ${
|
||||
user.is_active
|
||||
? 'bg-orange-50 dark:bg-orange-400/10 text-orange-600 dark:text-orange-400 hover:bg-orange-100 dark:hover:bg-orange-400/20'
|
||||
: 'bg-green-50 dark:bg-green-400/10 text-green-600 dark:text-green-400 hover:bg-green-100 dark:hover:bg-green-400/20'
|
||||
: 'bg-[var(--success-bg)] dark:bg-green-400/10 text-[var(--success)] dark:text-green-400 hover:bg-[var(--success-bg)] dark:hover:bg-green-400/20'
|
||||
}`}
|
||||
>
|
||||
{toggleStatusMut.isPending
|
||||
@@ -523,14 +523,14 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* ── User Profile (read-only) ──────────────────────────── */}
|
||||
<div className="cp-card p-6 space-y-4">
|
||||
<h2 className="text-sm font-semibold text-slate-700 dark:text-slate-300">پروفایل کاربر</h2>
|
||||
<h2 className="text-sm font-semibold text-[var(--text)]">پروفایل کاربر</h2>
|
||||
|
||||
{profileQ.isLoading ? (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{Array.from({ length: 6 }).map((_, i) => <div key={i} className="h-16 rounded-xl skeleton" />)}
|
||||
</div>
|
||||
) : !profile ? (
|
||||
<p className="text-sm text-slate-500 dark:text-slate-400 py-4">
|
||||
<p className="text-sm text-[var(--text-2)] py-4">
|
||||
این کاربر هنوز پروفایلی تکمیل نکرده است.
|
||||
</p>
|
||||
) : (
|
||||
@@ -552,8 +552,8 @@ export default function UserDetailPage() {
|
||||
</div>
|
||||
|
||||
{/* Medical history */}
|
||||
<div className="pt-2 border-t border-slate-100 dark:border-gray-700 space-y-3">
|
||||
<h3 className="text-sm font-semibold text-slate-700 dark:text-slate-300">سوابق پزشکی</h3>
|
||||
<div className="pt-2 border-t border-[var(--border)] space-y-3">
|
||||
<h3 className="text-sm font-semibold text-[var(--text)]">سوابق پزشکی</h3>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
{MEDICAL_SECTIONS.map(({ key, label, format, filter }) => {
|
||||
const raw = Array.isArray(profile.other?.[key]) ? (profile.other![key] as any[]) : [];
|
||||
@@ -561,14 +561,14 @@ export default function UserDetailPage() {
|
||||
.map(format)
|
||||
.filter((s) => s && s.trim());
|
||||
return (
|
||||
<div key={key} className="p-4 rounded-xl bg-slate-50 dark:bg-gray-800/60">
|
||||
<p className="text-[11px] font-medium text-slate-400 dark:text-slate-500 uppercase tracking-wide mb-1.5">{label}</p>
|
||||
<div key={key} className="p-4 rounded-xl bg-[var(--surface-2)] dark:bg-[var(--surface)]/60">
|
||||
<p className="text-[11px] font-medium text-[var(--text-3)] uppercase tracking-wide mb-1.5">{label}</p>
|
||||
{items.length === 0 ? (
|
||||
<p className="text-sm text-slate-400 dark:text-slate-500">موردی ثبت نشده</p>
|
||||
<p className="text-sm text-[var(--text-3)]">موردی ثبت نشده</p>
|
||||
) : (
|
||||
<ul className="flex flex-wrap gap-1.5">
|
||||
{items.map((text, i) => (
|
||||
<li key={i} className="text-xs px-2 py-0.5 rounded-lg bg-white dark:bg-gray-700 text-slate-600 dark:text-slate-300 border border-slate-200 dark:border-gray-600">
|
||||
<li key={i} className="text-xs px-2 py-0.5 rounded-lg bg-[var(--surface)] text-[var(--text-2)] border border-[var(--border)] dark:border-gray-600">
|
||||
{text}
|
||||
</li>
|
||||
))}
|
||||
@@ -606,19 +606,19 @@ export default function UserDetailPage() {
|
||||
<form id="edit-user-form" onSubmit={handleSubmit((v) => updateMut.mutate(v))} className="space-y-4">
|
||||
|
||||
{/* Avatar preview */}
|
||||
<div className="flex items-center gap-4 pb-4 border-b border-slate-100 dark:border-gray-700">
|
||||
<div className="flex items-center gap-4 pb-4 border-b border-[var(--border)]">
|
||||
<BigAvatar name={user.name} id={user.id} />
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-slate-800 dark:text-slate-200">
|
||||
<p className="text-sm font-semibold text-[var(--text)]">
|
||||
{user.name || 'نام ثبت نشده'}
|
||||
</p>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 font-mono" dir="ltr">{user.mobile_number}</p>
|
||||
<p className="text-xs text-[var(--text-2)] font-mono" dir="ltr">{user.mobile_number}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Name */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
|
||||
<label className="block text-sm font-medium text-[var(--text)] mb-1.5">
|
||||
نام و نام خانوادگی
|
||||
</label>
|
||||
<input
|
||||
@@ -627,12 +627,12 @@ export default function UserDetailPage() {
|
||||
className={`cp-input ${errors.name ? 'border-red-400 dark:border-red-500' : ''}`}
|
||||
{...register('name')}
|
||||
/>
|
||||
{errors.name && <p className="text-xs text-red-500 dark:text-red-400 mt-1">{errors.name.message}</p>}
|
||||
{errors.name && <p className="text-xs text-[var(--danger)] dark:text-red-400 mt-1">{errors.name.message}</p>}
|
||||
</div>
|
||||
|
||||
{/* Email */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
|
||||
<label className="block text-sm font-medium text-[var(--text)] mb-1.5">
|
||||
آدرس ایمیل
|
||||
</label>
|
||||
<input
|
||||
@@ -642,17 +642,17 @@ export default function UserDetailPage() {
|
||||
className={`cp-input text-left ${errors.email ? 'border-red-400 dark:border-red-500' : ''}`}
|
||||
{...register('email')}
|
||||
/>
|
||||
{errors.email && <p className="text-xs text-red-500 dark:text-red-400 mt-1">{errors.email.message}</p>}
|
||||
{errors.email && <p className="text-xs text-[var(--danger)] dark:text-red-400 mt-1">{errors.email.message}</p>}
|
||||
</div>
|
||||
|
||||
{/* Password */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
|
||||
<label className="block text-sm font-medium text-[var(--text)] mb-1.5">
|
||||
رمز عبور جدید
|
||||
<span className="text-xs font-normal text-slate-400 dark:text-slate-500 mr-1">(خالی = بدون تغییر)</span>
|
||||
<span className="text-xs font-normal text-[var(--text-3)] mr-1">(خالی = بدون تغییر)</span>
|
||||
</label>
|
||||
<div className="relative">
|
||||
<KeyIcon className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" />
|
||||
<KeyIcon className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-3)] pointer-events-none" />
|
||||
<input
|
||||
type="password"
|
||||
placeholder="حداقل ۶ کاراکتر"
|
||||
@@ -661,21 +661,21 @@ export default function UserDetailPage() {
|
||||
{...register('password')}
|
||||
/>
|
||||
</div>
|
||||
{errors.password && <p className="text-xs text-red-500 dark:text-red-400 mt-1">{errors.password.message}</p>}
|
||||
{errors.password && <p className="text-xs text-[var(--danger)] dark:text-red-400 mt-1">{errors.password.message}</p>}
|
||||
</div>
|
||||
|
||||
{/* Mobile (read-only) */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-1.5">
|
||||
<label className="block text-sm font-medium text-[var(--text)] mb-1.5">
|
||||
شماره موبایل
|
||||
<span className="text-xs font-normal text-slate-400 dark:text-slate-500 mr-1">(قابل تغییر نیست)</span>
|
||||
<span className="text-xs font-normal text-[var(--text-3)] mr-1">(قابل تغییر نیست)</span>
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={user.mobile_number}
|
||||
readOnly
|
||||
dir="ltr"
|
||||
className="cp-input text-left bg-slate-50 dark:bg-gray-800/60 text-slate-400 cursor-not-allowed"
|
||||
className="cp-input text-left bg-[var(--surface-2)] dark:bg-[var(--surface)]/60 text-[var(--text-3)] cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user