HR-ATS-Portal/frontend/src/api/jobStats.js

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,
}
}