UI fixes and job lifecycle actions
Deploy to S3 / deploy (push) Successful in 35s
Details
Deploy to S3 / deploy (push) Successful in 35s
Details
- Jobs list: close/reopen action per requisition with confirm on close - Settings: real timezone and currency option lists - Assigned-role chip: readable color pairing on soft background - Scrollbars: Chromium honors the styled ::-webkit-scrollbar again (scrollbar-color now gated to Firefox), slightly wider hit area - Topbar hamburger: hidden on desktop (specificity fix) and the drawer closes itself when the window grows past the mobile breakpoint - Content scroller: removed overscroll containment that killed wheel scrolling everywhere except the scrollbar thumb Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>pull/29/head
parent
1ae5249d18
commit
5575a1a6aa
|
|
@ -36,6 +36,16 @@ export function useNavOpen() {
|
|||
}
|
||||
}, [navOpen])
|
||||
|
||||
// The drawer only exists ≤900px (styles.css). If the window grows past the
|
||||
// breakpoint while it is open, nav-open would strand the scrim over the
|
||||
// desktop layout with body scroll locked — close it instead.
|
||||
useEffect(() => {
|
||||
const mq = window.matchMedia('(max-width: 900px)')
|
||||
const onChange = (e) => { if (!e.matches) setNavOpen(false) }
|
||||
mq.addEventListener('change', onChange)
|
||||
return () => mq.removeEventListener('change', onChange)
|
||||
}, [])
|
||||
|
||||
return [navOpen, setNavOpen]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -367,7 +367,10 @@ export default function CandidateProfile({
|
|||
<>
|
||||
<div style={LABEL}>Assigned Role</div>
|
||||
<div className="k-tags" style={{ marginBottom: 14 }}>
|
||||
<span className="tag" style={{ background: 'var(--primary-soft)', color: 'var(--primary-fg)' }}>
|
||||
{/* --primary-fg is the on-solid-primary text color (white in
|
||||
light theme) — on --primary-soft it was white-on-mint,
|
||||
unreadable. Soft chips pair with --primary (see .b-indigo). */}
|
||||
<span className="tag" style={{ background: 'var(--primary-soft)', color: 'var(--primary)' }}>
|
||||
{live.assigned_job_post.title}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -239,6 +239,27 @@ export default function Jobs() {
|
|||
{canEdit && (
|
||||
<button className="act-btn" data-tip="Edit" aria-label="Edit job" onClick={() => setEditing(j)}><Icon name="edit" /></button>
|
||||
)}
|
||||
{canEdit && (j.status === 'Closed' ? (
|
||||
<button
|
||||
className="act-btn"
|
||||
data-tip="Reopen"
|
||||
aria-label="Reopen job"
|
||||
disabled={setJobStatus.isPending}
|
||||
onClick={() => setJobStatus.mutate({ id: j.id, status: 'Open' })}
|
||||
><Icon name="refresh" /></button>
|
||||
) : (
|
||||
<button
|
||||
className="act-btn"
|
||||
data-tip="Close job"
|
||||
aria-label="Close job"
|
||||
disabled={setJobStatus.isPending}
|
||||
onClick={() => {
|
||||
if (window.confirm(`Close “${j.title}”? It stays on the board and can be reopened later.`)) {
|
||||
setJobStatus.mutate({ id: j.id, status: 'Closed' })
|
||||
}
|
||||
}}
|
||||
><Icon name="x-circle" /></button>
|
||||
))}
|
||||
<button className="act-btn" data-tip="Publish" aria-label="Publish job" onClick={() => navigate('/jobboard', { state: { publishJob: j.id } })}><Icon name="send" /></button>
|
||||
</div>
|
||||
),
|
||||
|
|
|
|||
|
|
@ -147,13 +147,35 @@ export default function Settings() {
|
|||
)
|
||||
}
|
||||
|
||||
/* The option label IS the stored value (org_settings rows hold the full string),
|
||||
so existing saved values like "(GMT-08:00) Pacific Time" must stay verbatim. */
|
||||
const TIMEZONES = [
|
||||
'(GMT-08:00) Pacific Time',
|
||||
'(GMT-07:00) Mountain Time',
|
||||
'(GMT-06:00) Central Time',
|
||||
'(GMT-05:00) Eastern Time',
|
||||
'(GMT+00:00) UTC',
|
||||
'(GMT+00:00) London',
|
||||
'(GMT+01:00) Central European Time',
|
||||
'(GMT+03:00) Arabia Standard Time',
|
||||
'(GMT+04:00) Gulf Standard Time',
|
||||
'(GMT+05:00) Pakistan Standard Time',
|
||||
'(GMT+05:30) India Standard Time',
|
||||
'(GMT+08:00) Singapore Standard Time',
|
||||
'(GMT+09:00) Japan Standard Time',
|
||||
'(GMT+10:00) Australian Eastern Time',
|
||||
]
|
||||
|
||||
// Codes match the offer form's currency set (Offers.jsx).
|
||||
const CURRENCIES = ['USD ($)', 'EUR (€)', 'GBP (£)', 'PKR (₨)', 'AED (د.إ)']
|
||||
|
||||
function General({ registerSave }) {
|
||||
const defaults = {
|
||||
'general.company_name': 'Utopia Brands Inc.',
|
||||
'general.website': 'https://utopiabrands.com',
|
||||
'general.industry': 'Consumer Goods',
|
||||
'general.company_size': '201–500',
|
||||
'general.timezone': '(GMT-08:00) Pacific Time',
|
||||
'general.timezone': '(GMT+05:00) Pakistan Standard Time',
|
||||
'general.currency': 'USD ($)',
|
||||
'general.auto_archive_stale_jobs': true,
|
||||
'general.duplicate_detection': true,
|
||||
|
|
@ -204,15 +226,13 @@ function General({ registerSave }) {
|
|||
<div className="form-field">
|
||||
<label>Default Time Zone</label>
|
||||
<select value={draft['general.timezone'] ?? ''} onChange={(e) => setField('general.timezone', e.target.value)}>
|
||||
<option>(GMT-08:00) Pacific Time</option>
|
||||
<option>(GMT-05:00) Eastern Time</option>
|
||||
<option>(GMT+00:00) UTC</option>
|
||||
{TIMEZONES.map((tz) => <option key={tz}>{tz}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-field">
|
||||
<label>Default Currency</label>
|
||||
<select value={draft['general.currency'] ?? ''} onChange={(e) => setField('general.currency', e.target.value)}>
|
||||
<option>USD ($)</option><option>EUR (€)</option><option>GBP (£)</option>
|
||||
{CURRENCIES.map((c) => <option key={c}>{c}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -224,14 +224,18 @@ input, select, textarea { font-family: inherit; font-size: var(--fs-base); color
|
|||
a { color: inherit; text-decoration: none; -webkit-tap-highlight-color: transparent; }
|
||||
img, svg, video, canvas { max-width: 100%; }
|
||||
|
||||
::-webkit-scrollbar { width: 10px; height: 10px; }
|
||||
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 20px; border: 2px solid transparent; background-clip: padding-box; }
|
||||
::-webkit-scrollbar { width: 14px; height: 14px; }
|
||||
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 20px; border: 3px solid transparent; background-clip: padding-box; }
|
||||
::-webkit-scrollbar-thumb:hover { background: var(--text-3); background-clip: padding-box; }
|
||||
::selection { background: var(--brand-lime); color: var(--brand-ink); }
|
||||
|
||||
/* Firefox / non-WebKit scrollbars (WebKit ignores these). */
|
||||
@supports (scrollbar-color: auto) {
|
||||
* { scrollbar-color: var(--border-strong) transparent; scrollbar-width: thin; }
|
||||
/* Firefox-only scrollbar colors. Chrome 121+ also passes
|
||||
@supports (scrollbar-color: auto), and any non-auto scrollbar-color/width
|
||||
makes Chromium IGNORE the ::-webkit-scrollbar rules above — the old
|
||||
`scrollbar-width: thin` here is what forced the skinny native bars.
|
||||
Gate on -moz-appearance so only Firefox takes this path. */
|
||||
@supports (-moz-appearance: none) {
|
||||
* { scrollbar-color: var(--border-strong) transparent; }
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
|
|
@ -444,7 +448,10 @@ table.data thead th, .rbac-matrix th, .cal-dow, .info-item .il,
|
|||
display: flex; align-items: center; gap: 16px; padding: 0 22px;
|
||||
position: sticky; top: 0; z-index: 50;
|
||||
}
|
||||
.menu-toggle { display: none; }
|
||||
/* Double class: .icon-btn { display: grid } is declared later with equal
|
||||
specificity and used to win, leaving the hamburger visible on desktop —
|
||||
where clicking it stranded the scrim over the page with scroll locked. */
|
||||
.icon-btn.menu-toggle { display: none; }
|
||||
.topbar-search { position: relative; flex: 1; max-width: 480px; display: flex; align-items: center; }
|
||||
.topbar-search > svg { position: absolute; left: 14px; width: 18px; height: 18px; color: var(--text-3); pointer-events: none; }
|
||||
.topbar-search input {
|
||||
|
|
@ -530,7 +537,11 @@ table.data thead th, .rbac-matrix th, .cal-dow, .info-item .il,
|
|||
.dd-scroll { max-height: 360px; overflow-y: auto; overscroll-behavior: contain; }
|
||||
|
||||
/* ================= CONTENT / PAGE ================= */
|
||||
.content { flex: 1; overflow-y: auto; overscroll-behavior-y: contain; -webkit-overflow-scrolling: touch; padding: 26px 30px 60px; }
|
||||
/* No overscroll-behavior here: #app is min-height, so .content never overflows
|
||||
and the DOCUMENT is the real scroller. `contain` on this (non-scrolling)
|
||||
scroll container blocked wheel chaining — the wheel was dead everywhere
|
||||
except dragging the scrollbar thumb. */
|
||||
.content { flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 26px 30px 60px; }
|
||||
.page { animation: fadeUp .3s ease; }
|
||||
@keyframes fadeUp { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
||||
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-5); margin-bottom: var(--space-6); flex-wrap: wrap; }
|
||||
|
|
@ -1363,7 +1374,7 @@ canvas { width: 100%; max-width: 100%; display: block; }
|
|||
.sidebar.mobile-open { transform: translateX(0); box-shadow: var(--shadow-lg); }
|
||||
/* The FAB floated over the scrim and stayed tappable behind the drawer. */
|
||||
.nav-open .ai-fab { display: none; }
|
||||
.menu-toggle { display: grid; }
|
||||
.icon-btn.menu-toggle { display: grid; }
|
||||
.search-kbd { display: none; }
|
||||
.content { padding: 20px max(16px, env(safe-area-inset-left)) 50px max(16px, env(safe-area-inset-right)); }
|
||||
.profile-meta { display: none; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue