Inbox: redesign the form applicant detail pane

Standard card language instead of the email-head strips: Contact &
application, Profile and Matching roles are now proper cards with
card-heads. The roles panel collapses three overlapping controls into
one flow - a single Browse roles action, with Assign appearing only
once a selection exists (a permanently disabled primary button read as
broken UI). Sheet provenance moves off the floating header corner into
the Imported from ... row N caption, and the action bar gains a
divider (email pane too, for consistency).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull/58/head
Talha Ahmed 2026-09-02 19:40:03 +05:00
parent ef8d77ccfe
commit fbfcbb4eb8
1 changed files with 101 additions and 112 deletions

View File

@ -1492,12 +1492,6 @@ function FormApplicantDetail({
{loading && <span className="cell-sub">Loading details</span>}
</div>
</div>
{i.rowNumber != null && (
<div style={{ textAlign: 'right' }}>
<div className="cell-sub">Sheet row</div>
<div className="fw-600">{i.rowNumber}</div>
</div>
)}
</div>
{(resumeHref || profileHref) && (
@ -1570,132 +1564,127 @@ function FormApplicantDetail({
marginBottom: 20,
}}
>
<div style={{ flex: '1 1 320px', minWidth: 0 }}>
<div className="email-head" style={{ borderRadius: '10px 10px 0 0' }}>Contact &amp; application</div>
<div
className="info-grid"
style={{
marginBottom: 20,
border: '1px solid var(--border)',
borderTop: 'none',
borderRadius: '0 0 10px 10px',
padding: '14px 16px',
background: 'var(--bg-elev)',
}}
>
<div className="info-item"><div className="il">Email</div><div className="iv">{orDash(i.email)}</div></div>
<div className="info-item"><div className="il">Phone</div><div className="iv">{orDash(i.phone)}</div></div>
<div className="info-item"><div className="il">Applied</div><div className="iv">{i.received ? fmtDate(i.received) : '—'}</div></div>
<div className="info-item"><div className="il">Source</div><div className="iv">{orDash(i.source)}</div></div>
<div className="info-item"><div className="il">Screened by</div><div className="iv">{orDash(i.screenedBy)}</div></div>
<div className="info-item"><div className="il">Notice period</div><div className="iv">{orDash(i.noticePeriod)}</div></div>
<div style={{ flex: '1 1 320px', minWidth: 0, display: 'flex', flexDirection: 'column', gap: 18 }}>
<div className="card" style={{ boxShadow: 'none' }}>
<div className="card-head"><h3>Contact &amp; application</h3></div>
<div className="card-body">
<div className="info-grid">
<div className="info-item"><div className="il">Email</div><div className="iv">{orDash(i.email)}</div></div>
<div className="info-item"><div className="il">Phone</div><div className="iv">{orDash(i.phone)}</div></div>
<div className="info-item"><div className="il">Applied</div><div className="iv">{i.received ? fmtDate(i.received) : '—'}</div></div>
<div className="info-item"><div className="il">Source</div><div className="iv">{orDash(i.source)}</div></div>
<div className="info-item"><div className="il">Screened by</div><div className="iv">{orDash(i.screenedBy)}</div></div>
<div className="info-item"><div className="il">Notice period</div><div className="iv">{orDash(i.noticePeriod)}</div></div>
</div>
</div>
</div>
<div className="email-head" style={{ borderRadius: '10px 10px 0 0' }}>Profile</div>
<div
className="info-grid"
style={{
marginBottom: 20,
border: '1px solid var(--border)',
borderTop: 'none',
borderRadius: '0 0 10px 10px',
padding: '14px 16px',
background: 'var(--bg-elev)',
}}
>
<div className="info-item"><div className="il">Gender</div><div className="iv">{orDash(i.gender)}</div></div>
<div className="info-item"><div className="il">Date of birth</div><div className="iv">{i.dateOfBirth ? fmtDate(i.dateOfBirth) : '—'}</div></div>
<div className="info-item"><div className="il">CNIC</div><div className="iv">{orDash(i.cnic)}</div></div>
<div className="info-item"><div className="il">Marital status</div><div className="iv">{orDash(i.maritalStatus)}</div></div>
<div className="info-item"><div className="il">Location</div><div className="iv">{orDash(location)}</div></div>
<div className="info-item"><div className="il">Education</div><div className="iv">{orDash(education)}</div></div>
<div className="info-item"><div className="il">Graduation</div><div className="iv">{orDash(i.graduationYear)}</div></div>
<div className="info-item"><div className="il">Other university</div><div className="iv">{orDash(i.universityOther)}</div></div>
<div className="info-item"><div className="il">Current salary</div><div className="iv">{orDash(i.currentSalary)}</div></div>
<div className="info-item"><div className="il">Expected salary</div><div className="iv">{orDash(i.expectedSalary)}</div></div>
<div className="card" style={{ boxShadow: 'none' }}>
<div className="card-head"><h3>Profile</h3></div>
<div className="card-body">
<div className="info-grid">
<div className="info-item"><div className="il">Gender</div><div className="iv">{orDash(i.gender)}</div></div>
<div className="info-item"><div className="il">Date of birth</div><div className="iv">{i.dateOfBirth ? fmtDate(i.dateOfBirth) : '—'}</div></div>
<div className="info-item"><div className="il">CNIC</div><div className="iv">{orDash(i.cnic)}</div></div>
<div className="info-item"><div className="il">Marital status</div><div className="iv">{orDash(i.maritalStatus)}</div></div>
<div className="info-item"><div className="il">Location</div><div className="iv">{orDash(location)}</div></div>
<div className="info-item"><div className="il">Education</div><div className="iv">{orDash(education)}</div></div>
<div className="info-item"><div className="il">Graduation</div><div className="iv">{orDash(i.graduationYear)}</div></div>
<div className="info-item"><div className="il">Other university</div><div className="iv">{orDash(i.universityOther)}</div></div>
<div className="info-item"><div className="il">Current salary</div><div className="iv">{orDash(i.currentSalary)}</div></div>
<div className="info-item"><div className="il">Expected salary</div><div className="iv">{orDash(i.expectedSalary)}</div></div>
</div>
</div>
</div>
{i.hrComments && (
<div className="card" style={{ boxShadow: 'none', background: 'var(--bg-sunken)', marginBottom: 8 }}>
<div className="card" style={{ boxShadow: 'none', background: 'var(--bg-sunken)' }}>
<div className="card-body">
<div className="fw-600" style={{ marginBottom: 6 }}>HR comment</div>
<p style={{ margin: 0 }}>{i.hrComments}</p>
</div>
</div>
)}
{i.sheet && (
<div className="cell-sub" style={{ marginTop: 12 }}>
Imported from {i.sheet}
</div>
)}
</div>
<div role="radiogroup" aria-label="Matching roles" style={{ flex: '0 1 360px', minWidth: 260 }}>
<div className="fw-600" style={{ marginBottom: 8 }}>Matching roles</div>
<div className="cell-sub" style={{ marginBottom: 10 }}>
Matched by position applied for: {orDash(i.position)}
<div
role="radiogroup"
aria-label="Matching roles"
className="card"
style={{ flex: '1 1 300px', minWidth: 260, boxShadow: 'none', alignSelf: 'start' }}
>
<div className="card-head">
<div>
<h3>Matching roles</h3>
<span className="ch-sub">Matched by position: {orDash(i.position)}</span>
</div>
</div>
{matchCards.length === 0 && !manualPost ? (
<EmptyState icon="alert" title="No matching roles">
<p>No job post title matches this position. Choose a role manually.</p>
<div style={{ display: 'flex', gap: 8, justifyContent: 'center', flexWrap: 'wrap', marginTop: 10 }}>
<div className="card-body">
{matchCards.length === 0 && !manualPost ? (
<EmptyState icon="target" title="No matching roles">
<p>No job post title matches this position pick one manually.</p>
</EmptyState>
) : (
<>
{matchCards.map(({ rank, post }) => (
<JobCard
key={post.id}
post={post}
rank={rank}
badge={`Match #${rank}`}
selected={String(selection) === String(post.id)}
onSelect={(id) => setSelection(String(id))}
/>
))}
{manualPost && (
<JobCard
post={manualPost}
rank={0}
manual
selected={String(selection) === String(manualPost.id)}
onSelect={(id) => setSelection(String(id))}
/>
)}
</>
)}
<div className="flex gap-8" style={{ flexWrap: 'wrap', marginTop: 12 }}>
<button
className={`btn ${matchCards.length === 0 && !manualPost ? 'btn-primary' : 'btn-secondary'}`}
style={{ flex: '1 1 auto' }}
disabled={!canEdit}
title={!canEdit ? 'Requires inbox.edit' : undefined}
onClick={() => setShowPicker(true)}
>
<Icon name="search" /> Browse roles
</button>
{/* Assign only renders once there is a selection to act on
a permanently disabled primary button read as broken UI. */}
{selection && selection !== i.assignedId && (
<button
className="btn btn-primary btn-sm"
disabled={!canEdit}
className="btn btn-primary"
style={{ flex: '1 1 auto' }}
disabled={!canAssign}
title={!canEdit ? 'Requires inbox.edit' : undefined}
onClick={() => setShowPicker(true)}
onClick={() => {
if (!selection || !canEdit) return
assignMutation.mutate({ recordId: i.id, jobPostId: selection })
}}
>
Choose a role
<Icon name="check" /> Assign{selectedPost?.title ? `${selectedPost.title}` : ''}
</button>
</div>
</EmptyState>
) : (
matchCards.map(({ rank, post }) => (
<JobCard
key={post.id}
post={post}
rank={rank}
badge={`Match #${rank}`}
selected={String(selection) === String(post.id)}
onSelect={(id) => setSelection(String(id))}
/>
))
)}
{manualPost && (
<JobCard
post={manualPost}
rank={0}
manual
selected={String(selection) === String(manualPost.id)}
onSelect={(id) => setSelection(String(id))}
/>
)}
<button
className="btn btn-secondary"
style={{ width: '100%', marginTop: 8 }}
disabled={!canEdit}
title={!canEdit ? 'Requires inbox.edit' : undefined}
onClick={() => setShowPicker(true)}
>
Choose a different role
</button>
<button
className="btn btn-primary"
style={{ width: '100%', marginTop: 8 }}
disabled={!canAssign}
title={!canEdit ? 'Requires inbox.edit' : undefined}
onClick={() => {
if (!selection || !canEdit) return
assignMutation.mutate({ recordId: i.id, jobPostId: selection })
}}
>
Assign
</button>
)}
</div>
</div>
</div>
</div>
<div className="flex gap-8" style={{ flexWrap: 'wrap' }}>
{i.sheet && (
<div className="cell-sub" style={{ marginBottom: 14 }}>
Imported from {i.sheet}{i.rowNumber != null ? ` · row ${i.rowNumber}` : ''}
</div>
)}
<div className="flex gap-8" style={{ flexWrap: 'wrap', borderTop: '1px solid var(--border)', paddingTop: 16 }}>
<button className="btn btn-primary" onClick={onImport} disabled={panelBusy || i.processing === 'Imported'}>
<Icon name="user-plus" /> {i.processing === 'Imported' ? 'Imported' : 'Import Candidate'}
</button>
@ -2081,7 +2070,7 @@ function ApplicationDetail({
</div>
</div>
<div className="flex gap-8" style={{ flexWrap: 'wrap' }}>
<div className="flex gap-8" style={{ flexWrap: 'wrap', borderTop: '1px solid var(--border)', paddingTop: 16 }}>
<button className="btn btn-primary" onClick={onImport} disabled={panelBusy || i.processing === 'Imported'}>
<Icon name="user-plus" /> {i.processing === 'Imported' ? 'Imported' : 'Import Candidate'}
</button>