From 80a3b290b9b70c7eb9d09ab5dfa453b425456138 Mon Sep 17 00:00:00 2001 From: "ahmed.mujtaba" Date: Fri, 11 Sep 2026 18:37:08 +0500 Subject: [PATCH] forms done --- .gitignore | 3 +- .../037_requisition_period_jd_entity.sql | 15 +++++++ frontend/src/api/forms.js | 4 +- frontend/src/screens/CandidateForms.jsx | 16 ++++--- frontend/src/screens/CandidateProfile.jsx | 2 +- frontend/src/screens/Requisitions.jsx | 45 ++++++++++++++++++- 6 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 backend/migrations/manual/037_requisition_period_jd_entity.sql diff --git a/.gitignore b/.gitignore index 58c95ff..34593ac 100644 --- a/.gitignore +++ b/.gitignore @@ -92,4 +92,5 @@ frontend/dist/index.html # nothing under either path that should be ignored. frontend/dist/** nginx.conf -smoke.test.mjs \ No newline at end of file +smoke.test.mjs +Annex** \ No newline at end of file diff --git a/backend/migrations/manual/037_requisition_period_jd_entity.sql b/backend/migrations/manual/037_requisition_period_jd_entity.sql new file mode 100644 index 0000000..327e473 --- /dev/null +++ b/backend/migrations/manual/037_requisition_period_jd_entity.sql @@ -0,0 +1,15 @@ +-- 037_requisition_period_jd_entity.sql +-- Annexure A revision 2 added three fields the original 020 table never had: +-- "If not permanent, specify the period From/To" on the position block, the +-- mandatory "JD Available Yes/No" flag (distinct from the free-text +-- job_description), and "Entity" alongside Employee Name/Department in the +-- internal-recommendation block. Matches Requisition in +-- backend/candidate_forms/models.py. Applied at startup by +-- alembic_setup.run_manual_sql() — needed because prod boots with +-- DB_AUTOGENERATE=false and never autogenerates new columns. + +ALTER TABLE app.requisitions + ADD COLUMN IF NOT EXISTS period_from date, + ADD COLUMN IF NOT EXISTS period_to date, + ADD COLUMN IF NOT EXISTS jd_available boolean, + ADD COLUMN IF NOT EXISTS entity varchar; diff --git a/frontend/src/api/forms.js b/frontend/src/api/forms.js index e5d310f..8168951 100644 --- a/frontend/src/api/forms.js +++ b/frontend/src/api/forms.js @@ -5,8 +5,8 @@ import { request } from '../lib/apiClient' The digitized hiring forms: Annexure A (Employee Requisition), and the two halves of Annexure E — Interview Analysis (technical + behavioral) and - Cultural Fit. Dual-key like assessments: exactly one of inbox_id / - manual_upload_candidate_id. + HR Evaluation (form_type stays "cultural_fit"). Dual-key like assessments: + exactly one of inbox_id / manual_upload_candidate_id. Permissioned with the interviews module tags (interviews.view to read, interviews.create to fill, interviews.edit to amend). Creating is diff --git a/frontend/src/screens/CandidateForms.jsx b/frontend/src/screens/CandidateForms.jsx index 740b13e..8384db4 100644 --- a/frontend/src/screens/CandidateForms.jsx +++ b/frontend/src/screens/CandidateForms.jsx @@ -1,7 +1,9 @@ -/* The Forms tab of the candidate profile modal — Interview Analysis + Cultural - Fit (the two halves of Annexure E), and the Offer (Annexure J fields on the - offers table). Employee Requisition (Annexure A) lives on the Requisitions - screen, not on a candidate. +/* The Forms tab of the candidate profile modal — Interview Analysis + HR + Evaluation (the two halves of Annexure E; form_type stays "cultural_fit" — + only its title/criteria grew to the fuller HR Evaluation section in + revision 2), and the Offer (Annexure J fields on the offers table). + Employee Requisition (Annexure A) lives on the Requisitions screen, not on + a candidate. Field and criterion labels are rendered from GET /forms/definitions — the backend is the single authority for the paper forms' exact wording. The @@ -161,7 +163,7 @@ export default function CandidateFormsTab({ userId, live }) { } const segTabs = [ { key: 'interview_analysis', label: 'Interview Analysis' }, - { key: 'cultural_fit', label: 'Cultural Fit' }, + { key: 'cultural_fit', label: 'HR Evaluation' }, ...(!isManager ? [{ key: 'offer', label: 'Offer' }] : []), ] @@ -233,7 +235,7 @@ function SummaryStrip({ summary, evalCount }) {
- + setRating.mutate(n)} atsScore={atsScore} recommendation={recommendation} - atsAction={canScoreAts && can('candidates.create') ? : null} + atsAction={canRerunAts && can('candidates.create') ? : null} /> : live ? ( <> diff --git a/frontend/src/screens/Requisitions.jsx b/frontend/src/screens/Requisitions.jsx index 4300761..16c7a6a 100644 --- a/frontend/src/screens/Requisitions.jsx +++ b/frontend/src/screens/Requisitions.jsx @@ -275,6 +275,9 @@ function blankForm() { date_needed: '', type: '', job_description: '', + period_from: '', + period_to: '', + jd_available: false, to_replace: '', grade: '', recruitment_title: '', @@ -284,6 +287,7 @@ function blankForm() { recommended_grade: '', employee_name: '', employee_department: '', + entity: '', initiated_by: '', initiated_date: '', recommended_by: '', @@ -310,6 +314,9 @@ function fromRow(row) { date_needed: toDateInput(pos.date_needed), type: pos.type || '', job_description: pos.job_description || '', + period_from: toDateInput(pos.period_from), + period_to: toDateInput(pos.period_to), + jd_available: pos.jd_available === true, to_replace: rep.to_replace || '', grade: rep.grade || '', recruitment_title: rep.title || '', @@ -319,6 +326,7 @@ function fromRow(row) { recommended_grade: rep.recommended_grade || '', employee_name: ref.employee_name || '', employee_department: ref.employee_department || '', + entity: ref.entity || '', initiated_by: row.initiated_by || '', initiated_date: toDateInput(row.initiated_date), recommended_by: row.recommended_by || '', @@ -330,7 +338,7 @@ function fromRow(row) { approved_by_svp: row.approved_by_svp === true, approved_by_date_svp: toDateInput(row.approved_by_date_svp), is_replacement: hasAny(rep, ['to_replace', 'grade', 'title', 'date_separated', 'justification', 'budget', 'recommended_grade']), - is_referral: hasAny(ref, ['employee_name', 'employee_department']), + is_referral: hasAny(ref, ['employee_name', 'employee_department', 'entity']), } } @@ -343,6 +351,9 @@ function toPayload(f) { date_needed: emptyToNull(f.date_needed), type: emptyToNull(f.type), job_description: emptyToNull(f.job_description), + period_from: emptyToNull(f.period_from), + period_to: emptyToNull(f.period_to), + jd_available: f.jd_available, }, initiated_by: emptyToNull(f.initiated_by), initiated_date: emptyToNull(f.initiated_date), @@ -377,10 +388,12 @@ function toPayload(f) { ? { employee_name: emptyToNull(f.employee_name), employee_department: emptyToNull(f.employee_department), + entity: emptyToNull(f.entity), } : { employee_name: null, employee_department: null, + entity: null, }, } return body @@ -468,6 +481,22 @@ function RequisitionEditor({ row, allowed, onClose, onSaved, toast }) { ))}
+
+ + set('period_from', e.target.value)} + /> +
+
+ + set('period_to', e.target.value)} + /> +