Nonclick untile load complete
CI / checks (push) Failing after 1m50s Details
CI / checks (pull_request) Failing after 1m55s Details

pull/79/head
ahmed.mujtaba 2026-09-07 18:51:49 +05:00
parent d00ebcf6f5
commit 5cc1dc1997
2 changed files with 44 additions and 25 deletions

View File

@ -1389,6 +1389,9 @@ export default function Inbox() {
queryFn: () => (selectedKind === 'form' ? fetchFormDetail(selectedId) : fetchMessageDetail(selectedId)), queryFn: () => (selectedKind === 'form' ? fetchFormDetail(selectedId) : fetchMessageDetail(selectedId)),
enabled: Boolean(selectedId), enabled: Boolean(selectedId),
}) })
// List rows paint immediately; GET-by-id is the full application. Block every
// control in the detail pane until that fetch succeeds. The queue stays live.
const detailLocked = Boolean(selectedId) && !detailQuery.isError && !detailQuery.isSuccess
const sidebar = useMemo(() => { const sidebar = useMemo(() => {
if (!isForms) return list if (!isForms) return list
@ -1959,32 +1962,40 @@ export default function Inbox() {
{friendlyAuthError(detailQuery.error, 'Request failed')} {friendlyAuthError(detailQuery.error, 'Request failed')}
</EmptyState> </EmptyState>
</div> </div>
) : selected?.kind === 'form' ? (
<FormApplicantDetail
item={selected}
loading={detailQuery.isPending}
busy={setState.isPending || markDuplicate.isPending}
canEdit={canEdit}
toast={toast}
onImport={() => importItem(selected)}
onMove={() => moveToPipeline(selected)}
onNote={() => setNoting(selected)}
onReject={() => reject(selected)}
onToggleDuplicate={() => toggleDuplicate(selected)}
/>
) : ( ) : (
<ApplicationDetail <div
item={selected} className={detailLocked ? 'inbox-detail-pending' : undefined}
loading={detailQuery.isPending} inert={detailLocked || undefined}
busy={setState.isPending || markDuplicate.isPending} aria-busy={detailLocked || undefined}
canEdit={canEdit} >
toast={toast} {selected?.kind === 'form' ? (
onImport={() => importItem(selected)} <FormApplicantDetail
onMove={() => moveToPipeline(selected)} item={selected}
onNote={() => setNoting(selected)} loading={detailLocked}
onReject={() => reject(selected)} busy={detailLocked || setState.isPending || markDuplicate.isPending}
onToggleDuplicate={() => toggleDuplicate(selected)} canEdit={!detailLocked && canEdit}
/> toast={toast}
onImport={() => importItem(selected)}
onMove={() => moveToPipeline(selected)}
onNote={() => setNoting(selected)}
onReject={() => reject(selected)}
onToggleDuplicate={() => toggleDuplicate(selected)}
/>
) : (
<ApplicationDetail
item={selected}
loading={detailLocked}
busy={detailLocked || setState.isPending || markDuplicate.isPending}
canEdit={!detailLocked && canEdit}
toast={toast}
onImport={() => importItem(selected)}
onMove={() => moveToPipeline(selected)}
onNote={() => setNoting(selected)}
onReject={() => reject(selected)}
onToggleDuplicate={() => toggleDuplicate(selected)}
/>
)}
</div>
)} )}
</div> </div>
</div> </div>

View File

@ -1032,6 +1032,14 @@ canvas { width: 100%; max-width: 100%; display: block; }
.inbox-split { grid-template-columns: minmax(280px, 34%) minmax(0, 1fr); } .inbox-split { grid-template-columns: minmax(280px, 34%) minmax(0, 1fr); }
/* Desktop keeps the two-pane split; only the phone layout below shows it. */ /* Desktop keeps the two-pane split; only the phone layout below shows it. */
.inbox-back { display: none; } .inbox-back { display: none; }
/* Detail pane is painted from the list row before GET-by-id returns. Block
every control inside it until that payload lands the queue list stays live. */
.inbox-detail-pending {
pointer-events: none;
user-select: none;
opacity: 0.55;
cursor: wait;
}
/* Phones: the stacked split nested two scroll wells inside the page scroll. /* Phones: the stacked split nested two scroll wells inside the page scroll.
Instead: one page scroll, and a selected application takes over from the Instead: one page scroll, and a selected application takes over from the
list the Back button returns to it (standard master-detail collapse). */ list the Back button returns to it (standard master-detail collapse). */