fix: smooth mobile edit transition and align modal validation behavior (#286)
* fix: reliable Escape key close for all modals via useEscapeKey hook - Add useEscapeKey hook (document-level keydown listener) - Retrofit all 12 modal/overlay components to use it - Remove redundant overlay onKeyDown Escape handlers - Simplify modal-content onKeyDown to plain stopPropagation - Replace MedDetailModal's capture-phase useEffect with 3 useEscapeKey calls - Replace SharedSchedule's inline useEffect with useEscapeKey - Add mandatory modal rules to UI Consistency skill - All 777 frontend + 569 backend tests pass * fix: smooth mobile edit transition and align modal validation behavior * fix: keep overlay keydown non-closing for Enter key * fix: show mobile name error when validation already exists * fix: restore app-level escape priority handling * fix: prioritize schedule lightbox on Escape
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/* biome-ignore-all lint/style/noNestedTernary: timeline rendering uses explicit UI-state branching */
|
||||
import { Bell, NotebookPen, Share2 } from "lucide-react";
|
||||
import { Bell, ClipboardList, NotebookPen, Share2 } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ConfirmModal, MedicationAvatar } from "../components";
|
||||
@@ -538,6 +538,17 @@ export function DashboardPage() {
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
{med?.prescriptionEnabled && (
|
||||
<>
|
||||
{" "}
|
||||
<span
|
||||
className="prescription-icon info-tooltip"
|
||||
data-tooltip={t("tooltips.hasPrescription")}
|
||||
>
|
||||
<ClipboardList size={13} aria-hidden="true" />
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
{med?.takenBy && med.takenBy.length > 0 && (
|
||||
<span className="med-taken-by-line">
|
||||
|
||||
@@ -76,12 +76,14 @@ export function MedicationsPage() {
|
||||
useUnsavedChangesWarning(formChanged);
|
||||
|
||||
// View mode: grid (default) or form (edit/new)
|
||||
const [viewMode, setViewMode] = useState<"grid" | "form">("grid");
|
||||
// If navigating in with editMedId, suppress rendering until the edit form is ready
|
||||
const [pendingEditTransition, setPendingEditTransition] = useState(() => searchParams.has("editMedId"));
|
||||
const [viewMode, setViewMode] = useState<"grid" | "form">(pendingEditTransition ? "form" : "grid");
|
||||
const [lightboxImage, setLightboxImage] = useState<{ src: string; alt: string } | null>(null);
|
||||
const [activeTab, setActiveTab] = useState<"general" | "stock" | "prescription" | "schedule">("general");
|
||||
|
||||
// Mobile modal state (declared early because it's used in useEffect below)
|
||||
const [showEditModal, setShowEditModal] = useState(false);
|
||||
const [showEditModal, setShowEditModal] = useState(pendingEditTransition && window.innerWidth <= 768);
|
||||
const showEditModalRef = useRef(false);
|
||||
useEffect(() => {
|
||||
showEditModalRef.current = showEditModal;
|
||||
@@ -705,6 +707,8 @@ export function MedicationsPage() {
|
||||
setActiveTab("general");
|
||||
startEdit(medicationToEdit, openEditModal);
|
||||
setViewMode("form");
|
||||
setPendingEditTransition(false);
|
||||
window.dispatchEvent(new Event("medassist:edit-transition-ready"));
|
||||
|
||||
const nextParams = new URLSearchParams(searchParams);
|
||||
nextParams.delete("editMedId");
|
||||
@@ -716,6 +720,11 @@ export function MedicationsPage() {
|
||||
return allMeds.find((med) => med.id === editingId) ?? null;
|
||||
}, [allMeds, editingId]);
|
||||
|
||||
// While navigating from detail modal to edit, render nothing until form is populated
|
||||
if (pendingEditTransition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={`med-grid-wrapper${viewMode === "form" ? " desktop-edit-open" : ""}`}>
|
||||
{/* ── Grid View: always visible medication cards ── */}
|
||||
|
||||
Reference in New Issue
Block a user