update draft issue
This commit is contained in:
@@ -19,7 +19,8 @@ import {
|
||||
PositionCustomRequirements, PositionOverview, PositionRequirements,
|
||||
} from '@/components/krow/PositionDetails';
|
||||
import { SkillSurface } from '@/components/skills/SkillSurface';
|
||||
import { usePublishPageContext } from '@/components/ai-assistant';
|
||||
import { useAssistantPanel, usePublishPageContext } from '@/components/ai-assistant';
|
||||
import { continueDraftRequest } from '@/lib/skills/draftFlow';
|
||||
import { AdminPage, SectionTitle } from '@/pages/admin/_shell';
|
||||
import { RoleGlyph } from '@/pages/admin/RoleGlyph';
|
||||
import { buildPosition, payLabel } from '@/pages/admin/positionInsights';
|
||||
@@ -505,7 +506,7 @@ function candidateCriteria(position) {
|
||||
/**
|
||||
* One position. The whole card is the target, so there is no hunting for a link.
|
||||
*
|
||||
* A draft opens where a draft belongs — back in the form that was writing it —
|
||||
* A draft continues where the work now happens — in the panel beside the list —
|
||||
* while everything else opens the drawer it always did. That is the only
|
||||
* difference between the two states on this card: same layout, same figures,
|
||||
* and the status pill the card has always drawn now reads Draft.
|
||||
@@ -608,8 +609,8 @@ function PositionCard({ position, onOpen, isRecent = false }) {
|
||||
: 'Manual'}
|
||||
</span>
|
||||
{/* A draft is unfinished work, so the card says what happens next on it:
|
||||
it goes back to the form, not to a hiring drawer for a role nobody
|
||||
can apply to yet. */}
|
||||
it is continued in Owliver, not opened in a hiring drawer for a role
|
||||
nobody can apply to yet. */}
|
||||
<span className="inline-flex items-center gap-1 text-[11px] font-semibold text-krow-blue">
|
||||
{isDraft ? 'Continue' : 'View position'}
|
||||
<ArrowRight className="h-3 w-3 transition-transform duration-base group-hover:translate-x-0.5" aria-hidden="true" />
|
||||
@@ -868,6 +869,8 @@ function PositionDrawer({ position, onClose, onViewCandidates, onOpenPosition })
|
||||
|
||||
export default function AdminPositions() {
|
||||
const navigate = useNavigate();
|
||||
/* How this page starts a turn in the panel beside it — see `openPosition`. */
|
||||
const { ask } = useAssistantPanel();
|
||||
|
||||
/* No create form on this page, in any form — no drawer, no modal, no route.
|
||||
Creating a position is a conversation with Owliver beside the list (see
|
||||
@@ -898,15 +901,21 @@ export default function AdminPositions() {
|
||||
const recentIds = useRecentlySavedIds(postings);
|
||||
|
||||
/**
|
||||
* Continuing a draft.
|
||||
* Continuing a draft, in the panel beside the list.
|
||||
*
|
||||
* The same authoring form, on the same route, told which record it is
|
||||
* finishing. Not a second editor and not a copy of the values: the form loads
|
||||
* the draft and saves back to it, so publishing from there is the one publish
|
||||
* action there has ever been.
|
||||
* It used to open `/admin/positions/new?draft=<id>`, which answered the wrong
|
||||
* question: the reader had not asked for the form back, they had asked to
|
||||
* finish the position — and the form is only where finishing it used to have
|
||||
* to happen. Owliver can generate the description, read and set the vetting
|
||||
* weights, and publish, all through the mutations the form calls, so the work
|
||||
* no longer needs the page it was written on.
|
||||
*
|
||||
* So the card asks Owliver instead, naming the record by id, and the URL stays
|
||||
* `/admin/positions`. The form is untouched and still reachable — someone who
|
||||
* opens `?draft=<id>` deliberately gets exactly what they always did.
|
||||
*/
|
||||
const openPosition = (p) => (p.status === 'draft'
|
||||
? navigate(`/admin/positions/new?draft=${encodeURIComponent(p.id)}`)
|
||||
? ask(continueDraftRequest(p))
|
||||
: setSelected(p));
|
||||
|
||||
/**
|
||||
@@ -964,7 +973,7 @@ export default function AdminPositions() {
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
const q = search.trim().toLowerCase();
|
||||
return rows
|
||||
const ordered = rows
|
||||
.filter((p) => {
|
||||
if (q && !`${p.title} ${p.company || ''} ${p.role_category} ${p.location}`.toLowerCase().includes(q)) return false;
|
||||
if (status !== 'all' && p.status !== status) return false;
|
||||
@@ -973,6 +982,27 @@ export default function AdminPositions() {
|
||||
return true;
|
||||
})
|
||||
.sort(SORTS[sort].compare);
|
||||
|
||||
/**
|
||||
* The most recently created position leads.
|
||||
*
|
||||
* A position saved from the panel is the thing the reader was just doing,
|
||||
* and under "needs attention" a draft sorts to the bottom — it has no
|
||||
* applicants, so it reads as the least urgent row on the board. It is not:
|
||||
* it is unfinished work they just put down.
|
||||
*
|
||||
* Only in the default ordering. Someone who has explicitly chosen "Title
|
||||
* A–Z" or "Most applicants" asked for that order and gets it, and no
|
||||
* preference is written either way — this is one hoist over the sorted
|
||||
* list, not a change to how anything is stored.
|
||||
*/
|
||||
if (sort !== 'attention' || ordered.length < 2) return ordered;
|
||||
|
||||
const newest = ordered.reduce(
|
||||
(best, p) => (new Date(p.created_date) > new Date(best.created_date) ? p : best),
|
||||
ordered[0]
|
||||
);
|
||||
return [newest, ...ordered.filter((p) => p.id !== newest.id)];
|
||||
}, [rows, search, status, department, location, sort]);
|
||||
|
||||
const isFiltered = Boolean(search) || [status, department, location].some((v) => v !== 'all');
|
||||
|
||||
Reference in New Issue
Block a user