feat: implement Portal component and refactor modals to use it for improved positioning
This commit is contained in:
@@ -350,7 +350,7 @@ function EditModal({ clinic, onClose, onSaved }: {
|
||||
|
||||
const [activeTab, setActiveTab] = useState<'basic' | 'tags' | 'social'>('basic');
|
||||
|
||||
return (
|
||||
return createPortal(
|
||||
<div className="overlay" onClick={onClose}>
|
||||
<div className="modal" style={{ maxWidth: 560, maxHeight: '90vh', overflowY: 'auto' }} onClick={e => e.stopPropagation()}>
|
||||
<div className="modal-head">
|
||||
@@ -455,7 +455,8 @@ function EditModal({ clinic, onClose, onSaved }: {
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1301,24 +1302,22 @@ export default function ClinicDetailPage() {
|
||||
onCancel={() => setDetachDoctorConfirm(null)}
|
||||
/>
|
||||
|
||||
{/* Edit modal — portal to escape Leaflet transform context */}
|
||||
{editOpen && createPortal(
|
||||
{/* Edit modal — self-portals to escape transform context */}
|
||||
{editOpen && (
|
||||
<EditModal
|
||||
clinic={clinic}
|
||||
onClose={() => setEditOpen(false)}
|
||||
onSaved={() => { qc.invalidateQueries({ queryKey: ['clinic-detail', uuid] }); qc.invalidateQueries({ queryKey: ['admin-clinics'] }); }}
|
||||
/>,
|
||||
document.body,
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Invite doctor modal */}
|
||||
{inviteOpen && uuid && createPortal(
|
||||
{inviteOpen && uuid && (
|
||||
<InviteDoctorModal
|
||||
clinicUuid={uuid}
|
||||
onClose={() => setInviteOpen(false)}
|
||||
onInvited={() => { qc.invalidateQueries({ queryKey: ['clinic-invitations', uuid] }); setDoctorsTab('invitations'); }}
|
||||
/>,
|
||||
document.body,
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Delete confirm */}
|
||||
|
||||
@@ -17,6 +17,7 @@ import type { ApiResponse, PaginatedResponse } from '../lib/api';
|
||||
import type { Clinic } from '../types';
|
||||
import { useAuthStore } from '../stores/authStore';
|
||||
import MobileInput from '../components/ui/MobileInput';
|
||||
import Portal from '../components/ui/Portal';
|
||||
import { formatDate, formatNumber, iranMobileSchema } from '../lib/utils';
|
||||
import Pagination from '../components/ui/Pagination';
|
||||
import ConfirmDialog from '../components/ui/ConfirmDialog';
|
||||
@@ -244,6 +245,7 @@ export default function ClinicsPage() {
|
||||
|
||||
{/* Add Modal */}
|
||||
{addOpen && (
|
||||
<Portal>
|
||||
<div className="overlay" onClick={() => setAddOpen(false)}>
|
||||
<div className="modal" style={{ maxWidth: 420 }} onClick={(e) => e.stopPropagation()}>
|
||||
<div className="modal-head">
|
||||
@@ -287,6 +289,7 @@ export default function ClinicsPage() {
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Portal>
|
||||
)}
|
||||
|
||||
<ConfirmDialog
|
||||
|
||||
@@ -4,6 +4,7 @@ import { CheckIcon, XMarkIcon, PhoneIcon } from '@heroicons/react/24/outline';
|
||||
import { toast } from 'sonner';
|
||||
import { api } from '../lib/api';
|
||||
import type { PaginatedResponse } from '../lib/api';
|
||||
import Portal from '../components/ui/Portal';
|
||||
import { formatDate } from '../lib/utils';
|
||||
import PageHeader from '../components/ui/PageHeader';
|
||||
import Pagination from '../components/ui/Pagination';
|
||||
@@ -204,6 +205,7 @@ export default function PreRegistrationsPage() {
|
||||
|
||||
{/* Reject Dialog */}
|
||||
{rejectTarget && (
|
||||
<Portal>
|
||||
<div className="overlay" onClick={() => setRejectTarget(null)}>
|
||||
<div className="modal" style={{ maxWidth: 420 }} onClick={(e) => e.stopPropagation()}>
|
||||
<div className="modal-head">
|
||||
@@ -232,6 +234,7 @@ export default function PreRegistrationsPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Portal>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { toast } from 'sonner';
|
||||
import { api } from '../lib/api';
|
||||
import Portal from '../components/ui/Portal';
|
||||
import type { ApiResponse, PaginatedResponse } from '../lib/api';
|
||||
import { formatDate, formatNumber, maskMobile } from '../lib/utils';
|
||||
import ConfirmDialog from '../components/ui/ConfirmDialog';
|
||||
@@ -101,6 +102,7 @@ function ChangeRoleModal({ user, onClose, onSave, loading }: {
|
||||
}) {
|
||||
const [selected, setSelected] = useState(getPrimaryRole(user.roles));
|
||||
return (
|
||||
<Portal>
|
||||
<div className="overlay" onClick={onClose}>
|
||||
<div className="modal" style={{ maxWidth: 400 }} onClick={(e) => e.stopPropagation()}>
|
||||
<div className="modal-head">
|
||||
@@ -140,6 +142,7 @@ function ChangeRoleModal({ user, onClose, onSave, loading }: {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Portal>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user