nxtgauge-backend-rust/apps/companies/src/handlers/admin.rs
Ashwin Kumar 7928e21a21 fix: resolve all compilation warnings and errors across services
- Remove duplicate departments/designations/employees handlers from users service (already in employees service)
- Fix all 9 profession admin handlers to use correct DB schema (display_name, bio, location, custom_data)
- Fix companies admin handler to match CompanyProfile DB model with all fields
- Fix customers admin handler to match Requirement model with preferred_date
- Fix missing serde_json imports and type annotations in admin handlers
- Add #[allow(dead_code)] for intentionally unused structs/fields
- Add test infrastructure: auth crypto tests (2 passing), test directory structure
- Zero compilation warnings across all services
2026-04-07 12:52:55 +02:00

269 lines
8.4 KiB
Rust

use crate::AppState;
use db::models::company::CompanyProfile;
use db::models::job::Job;
use db::models::application::Application;
use axum::{
extract::{Path, Query, State},
http::StatusCode,
response::IntoResponse,
routing::{get, patch},
Json, Router,
};
use chrono::{DateTime, Utc};
use contracts::auth_middleware::AuthUser;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
pub fn router() -> Router<AppState> {
Router::new()
.route("/", get(list_companies))
.route("/{id}", get(get_company))
.route("/{id}/approve", patch(approve_company))
.route("/{id}/reject", patch(reject_company))
.route("/{id}/suspend", patch(suspend_company))
.route("/jobs", get(list_jobs))
.route("/applications", get(list_applications))
}
#[derive(Deserialize)]
#[allow(dead_code)]
pub struct ListQuery {
pub q: Option<String>,
}
#[derive(Serialize)]
pub struct AdminCompanyRow {
pub id: Uuid,
pub user_id: Uuid,
pub company_name: String,
pub registration_number: Option<String>,
pub industry: Option<String>,
pub status: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl From<CompanyProfile> for AdminCompanyRow {
fn from(c: CompanyProfile) -> Self {
Self {
id: c.id,
user_id: c.user_id,
company_name: c.company_name,
registration_number: c.registration_number,
industry: c.industry,
status: c.status,
created_at: c.created_at,
updated_at: c.updated_at,
}
}
}
#[derive(Serialize)]
pub struct AdminJobRow {
pub id: Uuid,
pub title: String,
pub description: Option<String>,
pub company_id: Uuid,
pub company_name: String,
pub location: Option<String>,
pub job_type: Option<String>,
pub salary_min: Option<i32>,
pub salary_max: Option<i32>,
pub status: String,
pub applications_count: i64,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl From<Job> for AdminJobRow {
fn from(j: Job) -> Self {
Self {
id: j.id,
title: j.title,
description: Some(j.description),
company_id: j.company_id,
company_name: String::new(),
location: Some(j.location),
job_type: Some(j.job_type),
salary_min: j.salary_min,
salary_max: j.salary_max,
status: j.status,
applications_count: 0,
created_at: j.created_at,
updated_at: j.updated_at,
}
}
}
#[derive(Serialize)]
pub struct AdminApplicationRow {
pub id: Uuid,
pub job_id: Uuid,
pub job_title: String,
pub company_id: Uuid,
pub company_name: String,
pub applicant_id: Uuid,
pub applicant_name: String,
pub applicant_email: String,
pub status: String,
pub cover_letter: Option<String>,
pub resume_url: Option<String>,
pub applied_at: DateTime<Utc>,
pub created_at: DateTime<Utc>,
}
impl From<Application> for AdminApplicationRow {
fn from(a: Application) -> Self {
Self {
id: a.id,
job_id: a.job_id,
job_title: String::new(),
company_id: Uuid::nil(),
company_name: String::new(),
applicant_id: a.job_seeker_id,
applicant_name: String::new(),
applicant_email: String::new(),
status: a.status,
cover_letter: a.cover_letter,
resume_url: a.resume_url,
applied_at: a.applied_at,
created_at: a.updated_at,
}
}
}
async fn list_companies(
_auth: AuthUser,
State(state): State<AppState>,
Query(_q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let companies = sqlx::query_as!(
CompanyProfile,
r#"
SELECT id, user_id, company_name, registration_number, industry, website_url,
employee_count, business_type, gst_number, contact_name, contact_email,
contact_phone, address_line1, city, state, country, postal_code,
status, free_job_slots, purchased_job_slots, free_contact_views,
purchased_contact_views, created_at, updated_at
FROM company_profiles
ORDER BY created_at DESC
LIMIT 100
"#
)
.fetch_all(&state.pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
let list: Vec<AdminCompanyRow> = companies.into_iter().map(|c| c.into()).collect();
Ok(Json(list))
}
async fn get_company(
_auth: AuthUser,
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let company = sqlx::query_as!(
CompanyProfile,
r#"
SELECT id, user_id, company_name, registration_number, industry, website_url,
employee_count, business_type, gst_number, contact_name, contact_email,
contact_phone, address_line1, city, state, country, postal_code,
status, free_job_slots, purchased_job_slots, free_contact_views,
purchased_contact_views, created_at, updated_at
FROM company_profiles WHERE id = $1
"#,
id
)
.fetch_optional(&state.pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
match company {
Some(c) => Ok(Json(AdminCompanyRow::from(c))),
None => Err((StatusCode::NOT_FOUND, "Company not found".to_string())),
}
}
async fn approve_company(
_auth: AuthUser,
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
sqlx::query!("UPDATE company_profiles SET status = 'ACTIVE', updated_at = NOW() WHERE id = $1", id)
.execute(&state.pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
Ok(Json(serde_json::json!({ "status": "ACTIVE" })))
}
async fn reject_company(
_auth: AuthUser,
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
sqlx::query!("UPDATE company_profiles SET status = 'REJECTED', updated_at = NOW() WHERE id = $1", id)
.execute(&state.pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
Ok(Json(serde_json::json!({ "status": "REJECTED" })))
}
async fn suspend_company(
_auth: AuthUser,
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
sqlx::query!("UPDATE company_profiles SET status = 'SUSPENDED', updated_at = NOW() WHERE id = $1", id)
.execute(&state.pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
Ok(Json(serde_json::json!({ "status": "SUSPENDED" })))
}
async fn list_jobs(
_auth: AuthUser,
State(state): State<AppState>,
Query(_q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let jobs = sqlx::query_as!(
Job,
r#"
SELECT id, company_id, title, category, description, location, job_type,
salary_min, salary_max, experience_years, skills, status, rejection_reason,
expires_at, approved_at, approved_by, created_at, updated_at
FROM jobs
ORDER BY created_at DESC
LIMIT 100
"#
)
.fetch_all(&state.pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
let list: Vec<AdminJobRow> = jobs.into_iter().map(|j| j.into()).collect();
Ok(Json(list))
}
async fn list_applications(
_auth: AuthUser,
State(state): State<AppState>,
Query(_q): Query<ListQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let applications = sqlx::query_as!(
Application,
r#"
SELECT id, job_id, job_seeker_id, cover_letter, resume_url, status,
applied_at, updated_at, contact_viewed
FROM applications
ORDER BY applied_at DESC
LIMIT 100
"#
)
.fetch_all(&state.pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;
let list: Vec<AdminApplicationRow> = applications.into_iter().map(|a| a.into()).collect();
Ok(Json(list))
}