ui-polish 4/6: screen sweep B (17 remaining page screens)

AiAssistant, AiStudio, Analytics, Assessments, Calendar, CvImport, Help,
JobBoard, Notifications, Offers, Pipeline, Rbac, RecruiterHub, Reports,
Talent, TalentPool, Tasks — same bounded checklist:

- Every remaining hand-rolled .page-head replaced with <PageHeader/>;
  zero copies of the block remain in the codebase.
- JobBoard fixes its non-functional inline ellipsis (overflow without
  white-space never truncated) with .truncate + title.
- Tasks imports the shared PRIORITY_CLASS and labels its unlabeled
  card-head add button; remaining .act-btn tooltips mirrored into
  aria-labels (Assessments, JobBoard, Notifications, Offers, Talent).
- Assessments swaps a hand-rolled empty state for the imported
  EmptyState; Help drops an inline h2 font size the base rule now covers.
- Mechanical inline-style swaps to utilities on exact matches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull/29/head
Talha Ahmed 2026-08-25 21:51:26 +05:00
parent 4757895029
commit af6ef715c5
17 changed files with 142 additions and 169 deletions

View File

@ -1,5 +1,6 @@
import { useState } from 'react'
import Chat from '../app/ai/Chat'
import PageHeader from '../ui/PageHeader'
import { Icon } from '../ui/primitives'
export default function AiAssistant() {
@ -8,20 +9,18 @@ export default function AiAssistant() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">AI Assistant</h1>
<p className="page-sub">Your recruiting copilot powered by AI (interface preview)</p>
</div>
<div className="page-head-actions">
<PageHeader
title="AI Assistant"
sub="Your recruiting copilot — powered by AI (interface preview)"
actions={<>
<span className="integration-status pending">
<span className="pulse" />Model endpoint · Not connected
</span>
<button className="btn btn-secondary" onClick={() => setResetKey((k) => k + 1)}>
<Icon name="plus" /> New Chat
</button>
</div>
</div>
</>}
/>
<div className="card">
<div className="card-body">
<Chat resetKey={resetKey} />

View File

@ -1,5 +1,6 @@
import { useState } from 'react'
import Modal from '../ui/Modal'
import PageHeader from '../ui/PageHeader'
import { Badge, Icon } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { aiModules } from '../data/seed'
@ -11,20 +12,16 @@ export default function AiStudio() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">AI Studio</h1>
<p className="page-sub">
Next-generation AI modules designed and API-ready for backend integration
</p>
</div>
<div className="page-head-actions">
<PageHeader
title="AI Studio"
sub="Next-generation AI modules — designed and API-ready for backend integration"
actions={
<span className="integration-status pending"><span className="pulse" />{betaCount} in Beta</span>
</div>
</div>
}
/>
<div className="card brand-hero mb-18">
<div className="card-body" style={{ display: 'flex', alignItems: 'center', gap: 20, flexWrap: 'wrap' }}>
<div className="card-body flex items-center flex-wrap" style={{ gap: 20 }}>
<div className="ai-logo" style={{ margin: 0, width: 56, height: 56 }}><Icon name="sparkles" /></div>
<div style={{ flex: 1, minWidth: 220 }}>
<h2 style={{ fontSize: 19, marginBottom: 4 }}>Everything is API-ready</h2>

View File

@ -31,6 +31,7 @@ import { useMutation, useQueries, useQuery } from '@tanstack/react-query'
import Chart, { ChartLegend } from '../ui/Chart'
import Charts from '../lib/charts'
import DataTable from '../ui/DataTable'
import PageHeader from '../ui/PageHeader'
import { EmptyState, Icon } from '../ui/primitives'
import { qk } from '../lib/queryKeys'
import { friendlyAuthError } from '../lib/errors'
@ -396,12 +397,10 @@ export default function Analytics() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Analytics</h1>
<p className="page-sub">Deep-dive metrics across your recruitment funnel</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Analytics"
sub="Deep-dive metrics across your recruitment funnel"
actions={<>
<div className="pill-tabs">
{RANGES.map((r) => (
<span
@ -426,8 +425,8 @@ export default function Analytics() {
<option key={r.id} value={r.id}>{r.name}</option>
))}
</select>
</div>
</div>
</>}
/>
{kpisQuery.isError && (
<div className="card mb-18">

View File

@ -13,6 +13,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 { Avatar, Badge, EmptyState, FieldError, Icon, KpiCard, ProgressBar, ScoreChip } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { useAuth } from '../auth/AuthContext'
@ -168,10 +169,11 @@ export default function Assessments() {
key: '_a', label: 'Actions', align: 'right',
render: (a) => (
<div className="row-actions">
<button className="act-btn" data-tip="View" onClick={() => setViewing(a)}><Icon name="eye" /></button>
<button className="act-btn" data-tip="View" aria-label="View assessment" onClick={() => setViewing(a)}><Icon name="eye" /></button>
<button
className="act-btn"
data-tip="Remind"
aria-label="Send reminder"
disabled={!canEdit || remind.isPending}
title={!canEdit ? 'Requires assessments.edit' : undefined}
onClick={() => remind.mutate(a.id)}
@ -185,12 +187,10 @@ export default function Assessments() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Assessments</h1>
<p className="page-sub">Coding tests, take-homes, and evaluations</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Assessments"
sub="Coding tests, take-homes, and evaluations"
actions={
<button
className="btn btn-primary"
disabled={!canCreate}
@ -199,8 +199,8 @@ export default function Assessments() {
>
<Icon name="plus" /> Assign Assessment
</button>
</div>
</div>
}
/>
<div className="grid g-kpi mb-18">
<KpiCard label="Total Assigned" value={totalCount} icon="file" tone="i-indigo" />
@ -320,11 +320,9 @@ export default function Assessments() {
)}
</>
) : (
<div className="empty-state">
<Icon name="clock" />
<h3>Assessment not completed</h3>
<p>Results will appear once the candidate submits.</p>
</div>
<EmptyState icon="clock" title="Assessment not completed">
Results will appear once the candidate submits.
</EmptyState>
)}
</Modal>
)}

View File

@ -16,6 +16,7 @@ import { useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import PageHeader from '../ui/PageHeader'
import { Avatar, EmptyState, Icon } from '../ui/primitives'
import { qk } from '../lib/queryKeys'
import { friendlyAuthError } from '../lib/errors'
@ -122,15 +123,10 @@ export default function Calendar() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Calendar</h1>
<p className="page-sub">
Interview schedule at a glance
{monthQuery.isSuccess ? ` · ${events.length} this month` : ''}
</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Calendar"
sub={`Interview schedule at a glance${monthQuery.isSuccess ? ` · ${events.length} this month` : ''}`}
actions={<>
<div className="flex items-center gap-8">
<button className="btn btn-icon btn-secondary" onClick={() => step(-1)} aria-label="Previous month">
<Icon name="chevron-left" />
@ -152,8 +148,8 @@ export default function Calendar() {
>
<Icon name="plus" /> Schedule
</button>
</div>
</div>
</>}
/>
{monthQuery.isError ? (
<div className="card">

View File

@ -12,6 +12,7 @@
import { useRef, useState } from 'react'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import PageHeader from '../ui/PageHeader'
import { Badge, EmptyState, Icon, ScoreChip } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import JobCandidates from './JobCandidates'
@ -124,25 +125,20 @@ export default function CvImport() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">CV Import</h1>
<p className="page-sub">Upload resume PDFs parsed, scored against a job, and saved automatically</p>
</div>
<div className="page-head-actions">
<span className="integration-status pending"><span className="pulse" />AI Resume Scoring · Live</span>
</div>
</div>
<PageHeader
title="CV Import"
sub="Upload resume PDFs — parsed, scored against a job, and saved automatically"
actions={<span className="integration-status pending"><span className="pulse" />AI Resume Scoring · Live</span>}
/>
<div className="grid g-2-1">
<div>
<div className="card mb-18">
<div className="card-body">
<div className="flex items-center gap-8" style={{ marginBottom: 16 }}>
<div className="flex items-center gap-8 mb-16">
<span className="fw-600 text-sm" style={{ flexShrink: 0 }}>Score against</span>
<select
className="select"
style={{ flex: 1 }}
className="select flex-1"
value={jobId}
onChange={(e) => setJobId(e.target.value)}
>

View File

@ -1,4 +1,5 @@
import { useState } from 'react'
import PageHeader from '../ui/PageHeader'
import { Icon } from '../ui/primitives'
import { useToast } from '../ui/Toast'
@ -23,16 +24,11 @@ export default function Help() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Help Center</h1>
<p className="page-sub">Find answers and get support</p>
</div>
</div>
<PageHeader title="Help Center" sub="Find answers and get support" />
<div className="card brand-hero mb-18">
<div className="card-body" style={{ padding: 32, textAlign: 'center' }}>
<h2 style={{ fontSize: 22, marginBottom: 8 }}>How can we help you?</h2>
<h2 className="mb-8">How can we help you?</h2>
<p style={{ opacity: 0.85, marginBottom: 18 }}>
Search our knowledge base or browse the topics below
</p>

View File

@ -27,6 +27,7 @@ import { Link, useNavigate } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import DataTable from '../ui/DataTable'
import PageHeader from '../ui/PageHeader'
import { Badge, EmptyState, Icon, KpiCard } from '../ui/primitives'
import { qk } from '../lib/queryKeys'
import { friendlyAuthError } from '../lib/errors'
@ -235,11 +236,12 @@ export default function JobBoard() {
target="_blank"
rel="noreferrer noopener"
data-tip="Open live post"
aria-label="Open live post"
>
<Icon name="external" />
</a>
) : (
<button className="act-btn" data-tip="Not published yet" disabled>
<button className="act-btn" data-tip="Not published yet" aria-label="Not published yet" disabled>
<Icon name="external" />
</button>
)}
@ -250,12 +252,10 @@ export default function JobBoard() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Job Board</h1>
<p className="page-sub">Where each requisition was published, and whether it landed</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Job Board"
sub="Where each requisition was published, and whether it landed"
actions={<>
<Link className="btn btn-secondary" to="/analytics"><Icon name="trending-up" /> Analytics</Link>
<button
className="btn btn-primary"
@ -263,8 +263,8 @@ export default function JobBoard() {
>
<Icon name="send" /> Publish a Job
</button>
</div>
</div>
</>}
/>
<div className="grid g-kpi mb-18">
<KpiCard label="Total Posts" value={postsQuery.isPending ? '—' : stats.total} icon="layers" tone="i-indigo" />
@ -309,12 +309,12 @@ export default function JobBoard() {
<span className={`kpi-icn ${d.connected ? 'i-green' : 'i-indigo'}`} style={{ width: 34, height: 34, borderRadius: 9 }}>
<Icon name={d.connected ? 'check-circle' : 'layers'} />
</span>
<div style={{ minWidth: 0 }}>
<div className="lr-title" style={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>{d.name}</div>
<div className="min-w-0">
<div className="lr-title truncate" title={d.name}>{d.name}</div>
<div className="lr-sub">{d.service || (d.connected ? 'channel' : 'not connected')}</div>
</div>
</div>
<div className="flex items-center" style={{ justifyContent: 'space-between' }}>
<div className="flex items-center justify-between">
<span className="cell-sub">{d.posts} post{d.posts === 1 ? '' : 's'}</span>
<Badge className={d.connected ? 'b-green' : 'b-gray'}>
{d.connected ? 'Connected' : 'Available'}

View File

@ -1,6 +1,7 @@
import { useNavigate } from 'react-router-dom'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import PageHeader from '../ui/PageHeader'
import { EmptyState, Icon } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { qk } from '../lib/queryKeys'
@ -50,12 +51,10 @@ export default function Notifications() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Notifications</h1>
<p className="page-sub">Stay on top of hiring activity</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Notifications"
sub="Stay on top of hiring activity"
actions={
<button
className="btn btn-secondary"
disabled={markAll.isPending}
@ -63,8 +62,8 @@ export default function Notifications() {
>
<Icon name="check" /> Mark all read
</button>
</div>
</div>
}
/>
<div className="card">
{query.isPending && (
@ -107,6 +106,7 @@ export default function Notifications() {
<button
className="act-btn"
data-tip="Dismiss"
aria-label="Dismiss notification"
onClick={(e) => {
e.stopPropagation()
remove.mutate(n.id)

View File

@ -24,6 +24,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 { Avatar, Badge, EmptyState, FieldError, Icon, KpiCard } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { qk } from '../lib/queryKeys'
@ -224,12 +225,13 @@ export default function Offers() {
key: '_a', label: 'Actions', align: 'right',
render: (o) => (
<div className="row-actions">
<button className="act-btn" data-tip="View" onClick={() => setViewing(o)}>
<button className="act-btn" data-tip="View" aria-label="View offer" onClick={() => setViewing(o)}>
<Icon name="eye" />
</button>
<button
className="act-btn"
data-tip={o.status === 'draft' ? 'Send offer' : 'Resend'}
aria-label={o.status === 'draft' ? 'Send offer' : 'Resend offer'}
disabled={busy || ['accepted', 'declined'].includes(o.status)}
onClick={() => issue.mutate(o.id)}
>
@ -242,17 +244,15 @@ export default function Offers() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Offers</h1>
<p className="page-sub">Track offer letters and acceptance</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Offers"
sub="Track offer letters and acceptance"
actions={
<button className="btn btn-primary" onClick={() => setCreating(true)}>
<Icon name="plus" /> Create Offer
</button>
</div>
</div>
}
/>
<div className="grid g-kpi mb-18">
<KpiCard label="Offers Sent" value={allQuery.isPending ? '—' : stats.sent} icon="send" tone="i-indigo" />

View File

@ -18,6 +18,7 @@ import { useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { keepPreviousData, useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import PageHeader from '../ui/PageHeader'
import { Avatar, EmptyState, Icon, ScoreChip } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { useAuth } from '../auth/AuthContext'
@ -178,17 +179,15 @@ export default function Pipeline() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Pipeline</h1>
<p className="page-sub">
{canEdit
? 'Drag candidates between stages to update their status'
: 'Read-only — moving a candidate needs the pipeline.edit permission'}
{total > candidates.length && ` · showing ${candidates.length} of ${total} applications`}
</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Pipeline"
sub={<>
{canEdit
? 'Drag candidates between stages to update their status'
: 'Read-only — moving a candidate needs the pipeline.edit permission'}
{total > candidates.length && ` · showing ${candidates.length} of ${total} applications`}
</>}
actions={<>
<select className="select" value={jobId} onChange={(e) => setJobId(e.target.value)}>
<option value="">All Jobs</option>
{jobs.map((j) => (
@ -201,8 +200,8 @@ export default function Pipeline() {
>
<Icon name="plus" /> Add Candidate
</button>
</div>
</div>
</>}
/>
{board.isError ? (
<EmptyState title="Could not load the pipeline">

View File

@ -23,6 +23,7 @@ import { useEffect, useMemo, useState } from 'react'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import Modal from '../ui/Modal'
import PageHeader from '../ui/PageHeader'
import { Badge, EmptyState, FieldError, Icon } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { useFormState } from '../components/AuthLayout'
@ -129,19 +130,15 @@ export default function Rbac() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Access Control</h1>
<p className="page-sub">
Roles, permission bundles and the {totalTags || '104'}-tag vocabulary, live from the server
</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Access Control"
sub={<>Roles, permission bundles and the {totalTags || '104'}-tag vocabulary, live from the server</>}
actions={
<button className="btn btn-primary" onClick={() => setCreating(true)}>
<Icon name="plus" /> New Role
</button>
</div>
</div>
}
/>
{rolesQuery.isPending && (
<div className="card"><div className="card-body">

View File

@ -26,6 +26,7 @@ import { useQuery } from '@tanstack/react-query'
import Chart from '../ui/Chart'
import Charts from '../lib/charts'
import PageHeader from '../ui/PageHeader'
import { Avatar, EmptyState, Icon, KpiCard } from '../ui/primitives'
import { qk } from '../lib/queryKeys'
import { friendlyAuthError } from '../lib/errors'
@ -188,20 +189,18 @@ export default function RecruiterHub() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Recruiter Hub</h1>
<p className="page-sub">Per-recruiter performance, scoped server-side</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Recruiter Hub"
sub="Per-recruiter performance, scoped server-side"
actions={
<select className="select" value={selected.id} onChange={(e) => setRecruiterId(e.target.value)}>
{recruiters.map((x) => <option key={x.id} value={x.id}>{x.name}</option>)}
</select>
</div>
</div>
}
/>
<div className="card brand-hero mb-18">
<div className="card-body" style={{ display: 'flex', alignItems: 'center', gap: 18, flexWrap: 'wrap' }}>
<div className="card-body flex items-center flex-wrap" style={{ gap: 18 }}>
<Avatar name={name} initials={initialsOf(name)} color="rgba(255,255,255,.18)" className="avatar-lg" />
<div style={{ flex: 1, minWidth: 200 }}>
<div style={{ fontSize: 20, fontWeight: 700 }}>{name}</div>

View File

@ -36,6 +36,7 @@ import Chart, { ChartLegend } from '../ui/Chart'
import Charts from '../lib/charts'
import DataTable from '../ui/DataTable'
import Modal from '../ui/Modal'
import PageHeader from '../ui/PageHeader'
import { EmptyState, Icon, KpiCard, ProgressBar } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { qk } from '../lib/queryKeys'
@ -461,17 +462,15 @@ export default function Reports() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Reports</h1>
<p className="page-sub">Recruitment metrics across the selected window</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Reports"
sub="Recruitment metrics across the selected window"
actions={
<select className="select" value={rangeKey} onChange={(e) => setRangeKey(e.target.value)}>
{RANGES.map((r) => <option key={r.key} value={r.key}>{r.label}</option>)}
</select>
</div>
</div>
}
/>
{kpisQuery.isError && (
<div className="card mb-18"><div className="card-body">

View File

@ -12,6 +12,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 { Badge, EmptyState, Icon } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { qk } from '../lib/queryKeys'
@ -186,6 +187,7 @@ function ProfileCard({ p, onView, onDismiss, dismissing }) {
<a
className="act-btn"
data-tip="Open LinkedIn profile"
aria-label="Open LinkedIn profile"
href={p.linkedinUrl}
target="_blank"
rel="noreferrer"
@ -196,6 +198,7 @@ function ProfileCard({ p, onView, onDismiss, dismissing }) {
<button
className="act-btn"
data-tip="View profile"
aria-label="View profile"
onClick={(e) => { e.stopPropagation(); onView(p) }}
>
<Icon name="eye" />
@ -203,6 +206,7 @@ function ProfileCard({ p, onView, onDismiss, dismissing }) {
<button
className="act-btn"
data-tip="Dismiss"
aria-label="Dismiss profile"
disabled={dismissing}
onClick={(e) => { e.stopPropagation(); onDismiss(p) }}
>
@ -440,19 +444,15 @@ export default function Talent() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Find Talent</h1>
<p className="page-sub">Source matching LinkedIn profiles for a job via Apify</p>
</div>
<div className="page-head-actions">
<span className="integration-status pending"><span className="pulse" />LinkedIn Sourcing · Live</span>
</div>
</div>
<PageHeader
title="Find Talent"
sub="Source matching LinkedIn profiles for a job via Apify"
actions={<span className="integration-status pending"><span className="pulse" />LinkedIn Sourcing · Live</span>}
/>
<div className="card mb-18">
<div className="card-body">
<div className="flex items-center gap-8" style={{ flexWrap: 'wrap' }}>
<div className="flex items-center gap-8 flex-wrap">
<select
className="select"
style={{ flex: 1, minWidth: 180 }}

View File

@ -34,6 +34,7 @@ import { useMemo, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useQuery } from '@tanstack/react-query'
import PageHeader from '../ui/PageHeader'
import { Avatar, Badge, EmptyState, Icon, ScoreChip } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import CandidateProfile from './CandidateProfile'
@ -200,17 +201,15 @@ export default function TalentPool() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Talent Pool</h1>
<p className="page-sub">{pool.length} silver-medalists &amp; passive candidates to re-engage</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Talent Pool"
sub={<>{pool.length} silver-medalists &amp; passive candidates to re-engage</>}
actions={
<button className="btn btn-primary" onClick={() => toast('Talent campaign created', 'success')}>
<Icon name="send" /> Start Campaign
</button>
</div>
</div>
}
/>
<div className="card mb-18">
<div className="card-body" style={{ padding: 16 }}>

View File

@ -17,7 +17,8 @@ import { useNavigate } from 'react-router-dom'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import Modal from '../ui/Modal'
import { Badge, EmptyState, FieldError, Icon } from '../ui/primitives'
import PageHeader from '../ui/PageHeader'
import { Badge, EmptyState, FieldError, Icon, PRIORITY_CLASS } from '../ui/primitives'
import { useToast } from '../ui/Toast'
import { useAuth } from '../auth/AuthContext'
import { useFormState } from '../components/AuthLayout'
@ -28,7 +29,6 @@ import * as savedSearchesApi from '../api/savedSearches'
import { fmtDate, fmtShort } from '../data/seed'
const FILTERS = ['All', 'Open', 'Completed', 'Overdue', 'High', 'Medium', 'Low']
const PRIORITY_CLASS = { High: 'b-red', Medium: 'b-amber', Low: 'b-gray' }
// Mirrors backend/tasks/views.py CREATOR_ROLES the server is the enforcer,
// this only keeps the button honest.
const CREATOR_ROLES = ['system_administrator', 'hr_administrator', 'recruiter']
@ -156,12 +156,10 @@ export default function Tasks() {
return (
<div className="page">
<div className="page-head">
<div>
<h1 className="page-title">Tasks</h1>
<p className="page-sub">{openCount} open · {overdueCount} overdue</p>
</div>
<div className="page-head-actions">
<PageHeader
title="Tasks"
sub={`${openCount} open · ${overdueCount} overdue`}
actions={
<button
className="btn btn-primary"
disabled={!canCreate}
@ -170,8 +168,8 @@ export default function Tasks() {
>
<Icon name="plus" /> New Task
</button>
</div>
</div>
}
/>
<div className="grid g-2-1">
<div className="card">
@ -241,7 +239,7 @@ export default function Tasks() {
<div className="card" style={{ alignSelf: 'start' }}>
<div className="card-head">
<div><h3>Saved Searches</h3><span className="ch-sub">Quick candidate filters</span></div>
<button className="act-btn" onClick={() => setAddingSearch(true)}><Icon name="plus" /></button>
<button className="act-btn" aria-label="Add saved search" data-tip="Add saved search" onClick={() => setAddingSearch(true)}><Icon name="plus" /></button>
</div>
<div className="card-body">
{savedQuery.isPending && <EmptyState icon="bookmark" title="Loading…">Fetching saved searches.</EmptyState>}
@ -277,6 +275,7 @@ export default function Tasks() {
<button
className="act-btn"
data-tip="Delete"
aria-label="Delete saved search"
onClick={(e) => {
e.stopPropagation()
deleteSearch.mutate(s.id)