openProfile(c)}
>
|
@@ -392,7 +366,7 @@ export default function Candidates() {
|
- {c.email ?? '—'}
+ {c.email ?? '—'}
|
{c.isActive
@@ -948,7 +922,7 @@ function AddCandidate({ onClose, onSave, onInvalid }) {
- Drop the CV here or click to browse
+ Drop the CV here or click to browse
PDF only · text-based resumes · up to {MAX_CV_MB} MB
@@ -956,7 +930,7 @@ function AddCandidate({ onClose, onSave, onInvalid }) {
{cv && (
-
+
{cv.name}
{Math.max(1, Math.round(cv.size / 1024))} KB
diff --git a/frontend/src/screens/Dashboard.jsx b/frontend/src/screens/Dashboard.jsx
index c2d90d4..41329e2 100644
--- a/frontend/src/screens/Dashboard.jsx
+++ b/frontend/src/screens/Dashboard.jsx
@@ -5,7 +5,8 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import Chart, { ChartLegend } from '../ui/Chart'
import Charts from '../lib/charts'
import Dropdown from '../ui/Dropdown'
-import { Avatar, Badge, EmptyState, Icon, KpiTile, ProgressBar } from '../ui/primitives'
+import PageHeader from '../ui/PageHeader'
+import { Avatar, Badge, EmptyState, Icon, KpiTile, PRIORITY_CLASS, ProgressBar } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { useAuth } from '../auth/AuthContext'
import { qk } from '../lib/queryKeys'
@@ -17,7 +18,6 @@ import * as jobsApi from '../api/jobs'
import * as tasksApi from '../api/tasks'
const POLL_MS = 60_000
-const PRIORITY_CLASS = { High: 'b-red', Medium: 'b-amber', Low: 'b-gray' }
function asObject(data) {
return data && typeof data === 'object' && !Array.isArray(data) ? data : null
@@ -373,14 +373,10 @@ export default function Dashboard() {
return (
-
-
- {greetingFor()}, {firstName} 👋
-
- Here’s what’s happening with your hiring today — {todayLabel}
-
-
-
+ {greetingFor()}, {firstName} 👋>}
+ sub={<>Here’s what’s happening with your hiring today — {todayLabel}>}
+ actions={<>
Export
@@ -404,8 +400,8 @@ export default function Dashboard() {
Create Job
-
-
+ >}
+ />
{tiles.map((c) => )}
diff --git a/frontend/src/screens/Inbox.jsx b/frontend/src/screens/Inbox.jsx
index f634776..a506f9f 100644
--- a/frontend/src/screens/Inbox.jsx
+++ b/frontend/src/screens/Inbox.jsx
@@ -9,6 +9,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import Modal from '../ui/Modal'
import EmailBody, { looksLikeHtml } from '../ui/EmailBody'
+import PageHeader from '../ui/PageHeader'
import { Tabs } from '../ui/Tabs'
import { Pagination, pageWindow } from '../ui/DataTable'
import { Avatar, Badge, EmptyState, Icon, ScoreChip } from '../ui/primitives'
@@ -719,12 +720,10 @@ export default function Inbox() {
return (
-
-
- Recruitment Inbox
- Every candidate, every source — one unified queue
-
-
+
Microsoft Graph API · Connected
-
-
+ >}
+ />
diff --git a/frontend/src/screens/Interviews.jsx b/frontend/src/screens/Interviews.jsx
index 4f9720c..0cfe17c 100644
--- a/frontend/src/screens/Interviews.jsx
+++ b/frontend/src/screens/Interviews.jsx
@@ -25,6 +25,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import DataTable from '../ui/DataTable'
import Modal from '../ui/Modal'
+import PageHeader from '../ui/PageHeader'
import { Tabs } from '../ui/Tabs'
import { Avatar, Badge, EmptyState, FieldError, Icon, KpiCard, Stars } from '../ui/primitives'
import { useToast } from '../ui/Toast'
@@ -39,13 +40,6 @@ import { avatarColor, fmtShort, initials as initialsOf } from '../data/seed'
const FETCH_TOP = 200
-const STATUS_CLASS = {
- Scheduled: 'b-blue',
- Completed: 'b-green',
- Cancelled: 'b-red',
- 'No Show': 'b-amber',
-}
-
/** + -> one ISO instant, or null. */
function toInstant(date, time) {
if (!date) return null
@@ -229,14 +223,14 @@ export default function Interviews() {
},
{
key: 'status', label: 'Status', sortable: true,
- render: (iv) => {iv.status},
+ render: (iv) => {iv.status},
},
{
key: '_a', label: 'Actions', align: 'right',
render: (iv) => (
@@ -272,18 +266,16 @@ export default function Interviews() {
return (
-
-
- Interviews
- Manage and track all interview activity
-
-
+
Calendar View
setScheduling(true)}>
Schedule Interview
-
-
+ >}
+ />
@@ -484,14 +476,14 @@ function Scorecard({ interview: iv, onClose, onSaved, toast }) {
color={avatarColor(iv.candidate)}
className="avatar-lg"
/>
-
+
{iv.candidate}
{iv.type}{iv.jobTitle ? ` · ${iv.jobTitle}` : ''}
- {iv.status}
+ {iv.status}
-
+
-
+
-
+
-
+
{['Strong Hire', 'Hire', 'Lean Hire', 'No Hire'].map((r) => (
@@ -662,7 +654,7 @@ function ScheduleForm({ applications, loading, busy, onClose, onSubmit, toast })
-
+
Interviews attach to an application, so only candidates with an assigned job post appear here.
Duration, meeting mode and interviewers are not stored by the interview record.
diff --git a/frontend/src/screens/JobCandidates.jsx b/frontend/src/screens/JobCandidates.jsx
index 74c83a7..51c2320 100644
--- a/frontend/src/screens/JobCandidates.jsx
+++ b/frontend/src/screens/JobCandidates.jsx
@@ -92,7 +92,7 @@ function CandidateCard({ c, onView }) {
{c.experience != null ? `${c.experience} yrs` : '—'}
{c.currentCompany ?? ''}
{SOURCE_LABEL[c.source] ?? c.source}
- { e.stopPropagation(); onView(c) }}>
+ { e.stopPropagation(); onView(c) }}>
@@ -119,7 +119,7 @@ function FailedCard({ c, onView }) {
{c.errorCode ?? 'FAILED'}
{SOURCE_LABEL[c.source] ?? c.source}
- { e.stopPropagation(); onView(c) }}>
+ { e.stopPropagation(); onView(c) }}>
@@ -143,7 +143,7 @@ export function ScoredCandidateDetail({ candidate: c, jobTitle, onClose }) {
>
-
+
{displayName(c.name)}
{roleLine}
@@ -195,14 +195,14 @@ export function ScoredCandidateDetail({ candidate: c, jobTitle, onClose }) {
{tab === 'Job Match' && completed && (
<>
Job-Match Score
-
+
Match this candidate against the job the CV was scored for.
-
- {jobTitle ?? 'Selected job'}
+
+ {jobTitle ?? 'Selected job'}
{band && {band}}
@@ -212,7 +212,7 @@ export function ScoredCandidateDetail({ candidate: c, jobTitle, onClose }) {
Matched must-have skills ({c.matchedSkills.length})
-
+
{c.matchedSkills.length
? c.matchedSkills.map((s) => (
{s}
@@ -223,7 +223,7 @@ export function ScoredCandidateDetail({ candidate: c, jobTitle, onClose }) {
Missing must-have skills ({c.missingSkills.length})
-
+
{c.missingSkills.length
? c.missingSkills.map((s) => (
{s}
@@ -282,9 +282,9 @@ export default function JobCandidates({ jobId, jobTitle }) {
if (!jobId) return null
return (
-
-
- Candidates
+
+
+ Candidates
{rows.length} candidate{rows.length === 1 ? '' : 's'} · {scored} scored · {failed} failed
{jobTitle ? ` · vs ${jobTitle}` : ''}
diff --git a/frontend/src/screens/Jobs.jsx b/frontend/src/screens/Jobs.jsx
index e46b103..eca6816 100644
--- a/frontend/src/screens/Jobs.jsx
+++ b/frontend/src/screens/Jobs.jsx
@@ -14,6 +14,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import AiFieldAssist from '../ui/AiFieldAssist'
import DataTable from '../ui/DataTable'
import Modal from '../ui/Modal'
+import PageHeader from '../ui/PageHeader'
import { Badge, EmptyState, FieldError, Icon } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { useAuth } from '../auth/AuthContext'
@@ -221,11 +222,11 @@ export default function Jobs() {
key: '_a', label: 'Actions', align: 'right',
render: (j) => (
- setViewing(j)}>
+ setViewing(j)}>
{canEdit && (
- setEditing(j)}>
+ setEditing(j)}>
)}
- navigate('/jobboard', { state: { publishJob: j.id } })}>
+ navigate('/jobboard', { state: { publishJob: j.id } })}>
),
},
@@ -233,12 +234,10 @@ export default function Jobs() {
return (
-
-
- Jobs
- {jobs.length} requisitions · {openCount} currently open
-
-
+
{exporting ? 'Exporting…' : 'Export'}
@@ -247,8 +246,8 @@ export default function Jobs() {
Create Job
)}
-
-
+ >}
+ />
{jobsQuery.isPending && (
@@ -620,7 +619,7 @@ function JobForm({ departmentOptions, busy, onClose, onSubmit }) {
) : (
)}
-
+
{imageFile.name}
{Math.max(1, Math.round(imageFile.size / 1024))} KB
@@ -640,7 +639,7 @@ function JobForm({ departmentOptions, busy, onClose, onSubmit }) {
-
+
Saves the requisition to the board — publish to a channel later from the Job Board.
@@ -849,7 +848,7 @@ function RecruiterAssignment({ jobPostId, fallbackName, canEdit }) {
return (
<>
-
+
Recruiter ownership
{currentQuery.isError ? (
@@ -967,7 +966,7 @@ function JobDetail({
{j.description && (
<>
-
+
Description
{j.description}
diff --git a/frontend/src/screens/Managers.jsx b/frontend/src/screens/Managers.jsx
index 01ef2c7..06b5cd6 100644
--- a/frontend/src/screens/Managers.jsx
+++ b/frontend/src/screens/Managers.jsx
@@ -3,6 +3,7 @@ import { useLocation, useNavigate } from 'react-router-dom'
import { useMutation, useQuery } from '@tanstack/react-query'
import Modal from '../ui/Modal'
+import PageHeader from '../ui/PageHeader'
import { Avatar, Badge, EmptyState, Icon } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { useAuth } from '../auth/AuthContext'
@@ -46,12 +47,10 @@ export default function Managers() {
return (
-
-
- Hiring Managers
- {managers.length} managers · {totalReqs} active requisitions
-
-
+
{managersQuery.isPending && (
Fetching hiring managers.
@@ -71,9 +70,9 @@ export default function Managers() {
{managers.map((m) => (
-
+
-
+
{m.name}
{m.title || m.roleName || 'Hiring manager'}
@@ -83,8 +82,10 @@ export default function Managers() {
{m.teamSize ?? '—'}Team Size
-
- {m.email ? m.email.split('@')[0] : '—'}
+
+
+ {m.email || '—'}
+
setDetail(m)}>View
diff --git a/frontend/src/screens/Matching.jsx b/frontend/src/screens/Matching.jsx
index d28b2c0..e5c5016 100644
--- a/frontend/src/screens/Matching.jsx
+++ b/frontend/src/screens/Matching.jsx
@@ -12,6 +12,7 @@ import { useSearchParams } from 'react-router-dom'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import EmailBody, { looksLikeHtml } from '../ui/EmailBody'
+import PageHeader from '../ui/PageHeader'
import { Tabs } from '../ui/Tabs'
import { Avatar, Badge, EmptyState, Icon } from '../ui/primitives'
import { JobCard, PickRoleModal } from '../ui/SuggestedRoles'
@@ -375,15 +376,10 @@ export default function Matching() {
return (
-
-
- Job Matching
- Route applications to the right open role
-
-
+
{!canEdit && (
-
+
Your account does not hold inbox.edit, which the server requires to
assign, unassign, or retry a match. Controls below stay disabled.
@@ -554,7 +550,7 @@ function MatchingWorkspace({
-
+
{i.name}
{i.position}
@@ -577,7 +573,7 @@ function MatchingWorkspace({
marginBottom: 18,
}}
>
-
+
@@ -609,8 +605,8 @@ function MatchingWorkspace({
>
{matchFailed ? (
-
- {detail?.matchError || 'Matching failed for this application.'}
+
+ {detail?.matchError || 'Matching failed for this application.'}
) : (
-
+
AI verdict
{detail?.matchSummary || listRow?.matchSummary || 'No match summary yet.'}
@@ -639,7 +635,7 @@ function MatchingWorkspace({
)}
{whyOpen && (
-
+
{detail?.matchReasoning || listRow?.matchReasoning}
)}
@@ -649,7 +645,7 @@ function MatchingWorkspace({
{/* Email first: it is the application itself, and the resume is its
attachment. Reading order follows that. */}
{(detail?.subject || detail?.body) && (
-
+
Email
Subject: {detail.subject || '(no subject)'}
{looksLikeHtml(detail.bodyHtml) ? (
@@ -669,7 +665,7 @@ function MatchingWorkspace({
- Suggested roles
+ Suggested roles
{suggestionCards.length === 0 && !manualPost ? (
diff --git a/frontend/src/screens/Settings.jsx b/frontend/src/screens/Settings.jsx
index 1b338f6..2fd2492 100644
--- a/frontend/src/screens/Settings.jsx
+++ b/frontend/src/screens/Settings.jsx
@@ -9,6 +9,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import Modal from '../ui/Modal'
+import PageHeader from '../ui/PageHeader'
import { Tabs } from '../ui/Tabs'
import { Avatar, Badge, EmptyState, FieldError, Icon } from '../ui/primitives'
import { useToast } from '../ui/Toast'
@@ -115,23 +116,19 @@ export default function Settings() {
return (
-
-
- Settings
- Configure your workspace and team preferences
-
-
- {showOrgSave && (
- save.mutate()}
- >
- {save.isPending ? 'Saving…' : 'Save Changes'}
-
- )}
-
-
+ save.mutate()}
+ >
+ {save.isPending ? 'Saving…' : 'Save Changes'}
+
+ )}
+ />
({ key: t, label: t }))} />
@@ -264,14 +261,10 @@ function Users() {
{usersQuery.isError ? (
-
-
- Couldn’t load users
-
- {friendlyAuthError(usersQuery.error, 'The server did not return the user list.')}
- {' '}This tab needs the rbac_users.view permission.
-
-
+
+ {friendlyAuthError(usersQuery.error, 'The server did not return the user list.')}
+ {' '}This tab needs the rbac_users.view permission.
+
) : (
@@ -288,9 +281,9 @@ function Users() {
-
+
{u.name}
- {u.email}
+ {u.email}
|
|