feat: add doctor profile page with sidebar integration and routing
This commit is contained in:
@@ -1776,12 +1776,14 @@ type EditForm = z.infer<typeof editSchema>;
|
||||
|
||||
// ── Main Page ──────────────────────────────────────────────────────────────
|
||||
|
||||
export default function DoctorDetailPage() {
|
||||
const { uuid } = useParams<{ uuid: string }>();
|
||||
export default function DoctorDetailPage({ isOwnProfile = false }: { isOwnProfile?: boolean }) {
|
||||
const { uuid: paramUuid } = useParams<{ uuid: string }>();
|
||||
const navigate = useNavigate();
|
||||
const [searchParams] = useSearchParams();
|
||||
const qc = useQueryClient();
|
||||
const primaryRole = useAuthStore(s => s.primaryRole);
|
||||
const dbUuid = useAuthStore(s => s.dbUuid);
|
||||
const uuid = isOwnProfile ? (dbUuid ?? undefined) : paramUuid;
|
||||
|
||||
const [editOpen, setEditOpen] = useState(searchParams.get('edit') === '1');
|
||||
const [deleteOpen, setDeleteOpen] = useState(false);
|
||||
@@ -1967,12 +1969,25 @@ export default function DoctorDetailPage() {
|
||||
|
||||
{/* Breadcrumb */}
|
||||
<div className="flex items-center gap-2 text-sm text-slate-500 dark:text-slate-400">
|
||||
<button onClick={() => navigate('/admin/doctors')}
|
||||
className="flex items-center gap-1.5 hover:text-slate-800 dark:hover:text-slate-100 transition-colors">
|
||||
<ArrowRightIcon className="w-4 h-4" />پزشکان
|
||||
</button>
|
||||
<span>/</span>
|
||||
<span className="text-slate-700 dark:text-slate-300 font-medium">پروفایل پزشک</span>
|
||||
{isOwnProfile ? (
|
||||
<>
|
||||
<button onClick={() => navigate('/admin/dashboard')}
|
||||
className="flex items-center gap-1.5 hover:text-slate-800 dark:hover:text-slate-100 transition-colors">
|
||||
<ArrowRightIcon className="w-4 h-4" />داشبورد
|
||||
</button>
|
||||
<span>/</span>
|
||||
<span className="text-slate-700 dark:text-slate-300 font-medium">پروفایل من</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<button onClick={() => navigate('/admin/doctors')}
|
||||
className="flex items-center gap-1.5 hover:text-slate-800 dark:hover:text-slate-100 transition-colors">
|
||||
<ArrowRightIcon className="w-4 h-4" />پزشکان
|
||||
</button>
|
||||
<span>/</span>
|
||||
<span className="text-slate-700 dark:text-slate-300 font-medium">پروفایل پزشک</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Profile Card */}
|
||||
@@ -2026,39 +2041,43 @@ export default function DoctorDetailPage() {
|
||||
<PencilIcon className="w-4 h-4" />ویرایش
|
||||
</button>
|
||||
|
||||
{/* Toggle active button — styled by current state */}
|
||||
<button
|
||||
onClick={() => setToggleConfirm(true)}
|
||||
disabled={toggleMut.isPending}
|
||||
className={`inline-flex items-center gap-1.5 text-sm font-medium px-3 h-10 rounded-xl border transition-colors disabled:opacity-50 ${
|
||||
doctor.active
|
||||
? 'border-orange-300 dark:border-orange-500/40 text-orange-600 dark:text-orange-400 bg-orange-50 dark:bg-orange-500/10 hover:bg-orange-100 dark:hover:bg-orange-500/20'
|
||||
: 'border-emerald-300 dark:border-emerald-500/40 text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10 hover:bg-emerald-100 dark:hover:bg-emerald-500/20'
|
||||
}`}
|
||||
>
|
||||
{toggleMut.isPending
|
||||
? <span className="w-3.5 h-3.5 border-2 border-current/30 border-t-current rounded-full animate-spin" />
|
||||
: doctor.active
|
||||
? <XCircleIcon className="w-4 h-4" />
|
||||
: <CheckCircleIcon className="w-4 h-4" />
|
||||
}
|
||||
{doctor.active ? 'غیرفعال کردن' : 'فعال کردن'}
|
||||
</button>
|
||||
{/* Toggle active / Delete — فقط ادمین */}
|
||||
{!isOwnProfile && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setToggleConfirm(true)}
|
||||
disabled={toggleMut.isPending}
|
||||
className={`inline-flex items-center gap-1.5 text-sm font-medium px-3 h-10 rounded-xl border transition-colors disabled:opacity-50 ${
|
||||
doctor.active
|
||||
? 'border-orange-300 dark:border-orange-500/40 text-orange-600 dark:text-orange-400 bg-orange-50 dark:bg-orange-500/10 hover:bg-orange-100 dark:hover:bg-orange-500/20'
|
||||
: 'border-emerald-300 dark:border-emerald-500/40 text-emerald-600 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10 hover:bg-emerald-100 dark:hover:bg-emerald-500/20'
|
||||
}`}
|
||||
>
|
||||
{toggleMut.isPending
|
||||
? <span className="w-3.5 h-3.5 border-2 border-current/30 border-t-current rounded-full animate-spin" />
|
||||
: doctor.active
|
||||
? <XCircleIcon className="w-4 h-4" />
|
||||
: <CheckCircleIcon className="w-4 h-4" />
|
||||
}
|
||||
{doctor.active ? 'غیرفعال کردن' : 'فعال کردن'}
|
||||
</button>
|
||||
|
||||
<div className="relative" data-dmenu>
|
||||
<button data-dmenu onClick={() => setMenuOpen(!menuOpen)}
|
||||
className="cp-btn-secondary h-10 w-10 flex items-center justify-center px-0">
|
||||
<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-dmenu>
|
||||
<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">
|
||||
<TrashIcon className="w-4 h-4 shrink-0" />حذف این پزشک
|
||||
<div className="relative" data-dmenu>
|
||||
<button data-dmenu onClick={() => setMenuOpen(!menuOpen)}
|
||||
className="cp-btn-secondary h-10 w-10 flex items-center justify-center px-0">
|
||||
<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-dmenu>
|
||||
<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">
|
||||
<TrashIcon className="w-4 h-4 shrink-0" />حذف این پزشک
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import DoctorDetailPage from './DoctorDetailPage';
|
||||
|
||||
export default function DoctorProfilePage() {
|
||||
return <DoctorDetailPage isOwnProfile />;
|
||||
}
|
||||
Reference in New Issue
Block a user