Spaces:
Running
Running
Recruiters can delete a job
Browse files- frontend/src/lib/i18n.tsx +4 -2
- frontend/src/pages/JobEditor.tsx +7 -1
- platform_app/routes.py +11 -0
frontend/src/lib/i18n.tsx
CHANGED
|
@@ -141,7 +141,8 @@ const en = {
|
|
| 141 |
skills_pool: "Skills across all answers", jobs_table: "Jobs at a glance", export_csv: "Export CSV", c_job: "Job",
|
| 142 |
empty_company: "Post your first job to start receiving interviews. Public jobs appear on the EvalBot job board, and every job gets a link you can share anywhere.",
|
| 143 |
job_stats: "This job in numbers", search_candidates: "Search candidates", min_score: "Minimum score",
|
| 144 |
-
co_new_job_cta: "Post a job", out_of_100: "out of 100",
|
|
|
|
| 145 |
};
|
| 146 |
|
| 147 |
type Dict = typeof en;
|
|
@@ -287,7 +288,8 @@ const ar: Dict = {
|
|
| 287 |
skills_pool: "المهارات عبر كل الإجابات", jobs_table: "الوظائف باختصار", export_csv: "تصدير CSV", c_job: "الوظيفة",
|
| 288 |
empty_company: "انشر أول وظيفة لتبدأ في استقبال المقابلات. الوظائف العامة تظهر في لوحة وظائف EvalBot، ولكل وظيفة رابط تشاركه في أي مكان.",
|
| 289 |
job_stats: "الوظيفة بالأرقام", search_candidates: "ابحث في المرشحين", min_score: "أقل درجة",
|
| 290 |
-
co_new_job_cta: "انشر وظيفة", out_of_100: "من 100",
|
|
|
|
| 291 |
};
|
| 292 |
|
| 293 |
export type Lang = "en" | "ar";
|
|
|
|
| 141 |
skills_pool: "Skills across all answers", jobs_table: "Jobs at a glance", export_csv: "Export CSV", c_job: "Job",
|
| 142 |
empty_company: "Post your first job to start receiving interviews. Public jobs appear on the EvalBot job board, and every job gets a link you can share anywhere.",
|
| 143 |
job_stats: "This job in numbers", search_candidates: "Search candidates", min_score: "Minimum score",
|
| 144 |
+
co_new_job_cta: "Post a job", out_of_100: "out of 100", delete_job: "Delete job",
|
| 145 |
+
delete_job_confirm: "Delete this job and all of its interviews? This can't be undone. To stop new applications but keep the results, set the status to Closed instead.",
|
| 146 |
};
|
| 147 |
|
| 148 |
type Dict = typeof en;
|
|
|
|
| 288 |
skills_pool: "المهارات عبر كل الإجابات", jobs_table: "الوظائف باختصار", export_csv: "تصدير CSV", c_job: "الوظيفة",
|
| 289 |
empty_company: "انشر أول وظيفة لتبدأ في استقبال المقابلات. الوظائف العامة تظهر في لوحة وظائف EvalBot، ولكل وظيفة رابط تشاركه في أي مكان.",
|
| 290 |
job_stats: "الوظيفة بالأرقام", search_candidates: "ابحث في المرشحين", min_score: "أقل درجة",
|
| 291 |
+
co_new_job_cta: "انشر وظيفة", out_of_100: "من 100", delete_job: "حذف الوظيفة",
|
| 292 |
+
delete_job_confirm: "حذف هذه الوظيفة وكل مقابلاتها؟ لا يمكن التراجع عن ذلك. لإيقاف التقديم مع الاحتفاظ بالنتائج، اجعل الحالة «مغلقة» بدلاً من الحذف.",
|
| 293 |
};
|
| 294 |
|
| 295 |
export type Lang = "en" | "ar";
|
frontend/src/pages/JobEditor.tsx
CHANGED
|
@@ -144,7 +144,13 @@ export default function JobEditor() {
|
|
| 144 |
</Panel>
|
| 145 |
|
| 146 |
{error && <ErrorNote>{error}</ErrorNote>}
|
| 147 |
-
<div className="flex justify-end gap-3">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
<Button variant="quiet" onClick={() => nav(-1)}>{t("cancel")}</Button>
|
| 149 |
<Button size="lg" onClick={save} busy={busy} disabled={title.trim().length < 2 || !qs.some((x) => x.text.trim())}>{t("save_job")}</Button>
|
| 150 |
</div>
|
|
|
|
| 144 |
</Panel>
|
| 145 |
|
| 146 |
{error && <ErrorNote>{error}</ErrorNote>}
|
| 147 |
+
<div className="flex flex-wrap justify-end gap-3">
|
| 148 |
+
{id && (
|
| 149 |
+
<Button variant="quiet" className="me-auto text-danger hover:bg-danger-soft" onClick={async () => {
|
| 150 |
+
if (!window.confirm(t("delete_job_confirm"))) return;
|
| 151 |
+
try { await api.del(`/jobs/${id}`); nav("/app/jobs"); } catch (e: any) { setError(e.message); }
|
| 152 |
+
}}><Trash2 size={16} /> {t("delete_job")}</Button>
|
| 153 |
+
)}
|
| 154 |
<Button variant="quiet" onClick={() => nav(-1)}>{t("cancel")}</Button>
|
| 155 |
<Button size="lg" onClick={save} busy={busy} disabled={title.trim().length < 2 || !qs.some((x) => x.text.trim())}>{t("save_job")}</Button>
|
| 156 |
</div>
|
platform_app/routes.py
CHANGED
|
@@ -541,6 +541,17 @@ def update_job(jid: str, body: JobIn, user: Dict = Depends(recruiter)):
|
|
| 541 |
return q1(f"update jobs set {sets} where id = %(jid)s returning *", {**d, "jid": jid})
|
| 542 |
|
| 543 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 544 |
@router.get("/jobs/{jid}/candidates")
|
| 545 |
def job_candidates(jid: str, user: Dict = Depends(recruiter)):
|
| 546 |
_job(jid, user)
|
|
|
|
| 541 |
return q1(f"update jobs set {sets} where id = %(jid)s returning *", {**d, "jid": jid})
|
| 542 |
|
| 543 |
|
| 544 |
+
@router.delete("/jobs/{jid}")
|
| 545 |
+
def delete_job(jid: str, user: Dict = Depends(recruiter)):
|
| 546 |
+
"""Delete a job together with its interviews (only possible while nobody has been hired for it)."""
|
| 547 |
+
_job(jid, user)
|
| 548 |
+
if q1("select 1 from sessions s join reviews r on r.session_id = s.id where s.job_id = %s and r.status = 'hired'", [jid]):
|
| 549 |
+
raise HTTPException(400, "Someone was hired for this job. Close it instead of deleting it.")
|
| 550 |
+
q("delete from sessions where job_id = %s", [jid])
|
| 551 |
+
q("delete from jobs where id = %s", [jid])
|
| 552 |
+
return {"ok": True}
|
| 553 |
+
|
| 554 |
+
|
| 555 |
@router.get("/jobs/{jid}/candidates")
|
| 556 |
def job_candidates(jid: str, user: Dict = Depends(recruiter)):
|
| 557 |
_job(jid, user)
|