66 lines
2.3 KiB
JavaScript
66 lines
2.3 KiB
JavaScript
/* ============================================================
|
|
jobStats.js — per-job pipeline stage counts (GET /job/stats/fetch).
|
|
|
|
Live aggregation over inbox + manual upload + unpromoted sheet rows,
|
|
deduped by email. Needs jobs.view or pipeline.view.
|
|
============================================================ */
|
|
|
|
import { request } from '../lib/apiClient'
|
|
import { toDate } from '../lib/format'
|
|
import { REQUISITION_STATUSES } from './jobs'
|
|
|
|
const REQ_STATUS_LABEL = Object.fromEntries(REQUISITION_STATUSES.map((s) => [s.value, s.label]))
|
|
|
|
/**
|
|
* List or single-job stage counts.
|
|
* Omit jobPostId for a paged list; pass jobPostId for one object.
|
|
*/
|
|
export function list({ jobPostId, search, ids, top, skip, activeOnly } = {}) {
|
|
return request('/job/stats/fetch', {
|
|
params: {
|
|
job_post_id: jobPostId,
|
|
search,
|
|
ids: Array.isArray(ids) ? ids.join(',') : ids,
|
|
top,
|
|
skip,
|
|
active_only: activeOnly,
|
|
},
|
|
})
|
|
}
|
|
|
|
/** Whole days from created_at to the browser clock. Null if timestamp missing. */
|
|
export function daysOpen(createdAt, now = Date.now()) {
|
|
if (!createdAt) return null
|
|
const start = toDate(createdAt)?.getTime()
|
|
if (!Number.isFinite(start)) return null
|
|
return Math.max(0, Math.floor((now - start) / 86_400_000))
|
|
}
|
|
|
|
/** API row -> what Progress cards and the table render. */
|
|
export function toJobStatsView(row) {
|
|
const createdAt = row.created_at || null
|
|
return {
|
|
id: row.job_post_id,
|
|
title: row.title || 'Untitled role',
|
|
department: row.department || null,
|
|
location: row.location || null,
|
|
status: REQ_STATUS_LABEL[row.requisition_status] ?? row.requisition_status ?? '—',
|
|
requisitionStatus: row.requisition_status,
|
|
recruiterId: row.current_recruiter_id || null,
|
|
recruiterName: row.recruiter_name || null,
|
|
createdAt,
|
|
daysOpen: daysOpen(createdAt),
|
|
total: Number(row.total_applicants) || 0,
|
|
shortlist: Number(row.shortlisting) || 0,
|
|
screened: Number(row.screened) || 0,
|
|
assessment: Number(row.assessment) || 0,
|
|
interviewed: Number(row.interviewed) || 0,
|
|
offered: Number(row.offered) || 0,
|
|
onHold: Number(row.on_hold) || 0,
|
|
rejected: Number(row.rejected) || 0,
|
|
approved: Number(row.approved) || 0,
|
|
hired: Number(row.hired) || 0,
|
|
reapplied: Number(row.reapplied) || 0,
|
|
}
|
|
}
|