use crate::AppState; use axum::{ extract::{Path, Query, State}, http::StatusCode, response::IntoResponse, routing::{get, patch}, Json, Router, }; use contracts::auth_middleware::AuthUser; use serde::{Deserialize, Serialize}; use uuid::Uuid; pub fn router() -> Router { 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)) } #[derive(Deserialize)] pub struct ListQuery { pub q: Option, } #[derive(Serialize)] pub struct AdminCompanyRow { pub id: Uuid, pub user_id: Uuid, pub company_name: String, pub registration_number: Option, pub industry: Option, pub status: String, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, } #[derive(Serialize)] pub struct AdminCompanyDetail { pub id: Uuid, pub user_id: Uuid, pub company_name: String, pub registration_number: Option, pub industry: Option, pub website_url: Option, pub employee_count: Option, pub business_type: Option, pub gst_number: Option, pub contact_name: Option, pub contact_email: Option, pub contact_phone: Option, pub address_line1: Option, pub city: Option, pub state: Option, pub country: String, pub postal_code: Option, pub status: String, pub free_job_slots: i32, pub purchased_job_slots: i32, pub free_contact_views: i32, pub purchased_contact_views: i32, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, } #[derive(Deserialize)] pub struct ApproveRejectRequest { pub reason: Option, } async fn list_companies( _auth: AuthUser, State(state): State, Query(q): Query, ) -> Result { let search = q.q.as_deref().unwrap_or_default().to_lowercase(); let companies = sqlx::query_as!( AdminCompanyRow, r#" SELECT id, user_id, company_name, registration_number, industry, status, created_at, updated_at FROM company_profiles WHERE ($1 = '' OR LOWER(company_name) LIKE '%' || $1 || '%') ORDER BY created_at DESC LIMIT 100 "#, search ) .fetch_all(&state.pool) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?; Ok(Json(companies)) } async fn get_company( _auth: AuthUser, State(state): State, Path(id): Path, ) -> Result { let company = sqlx::query_as!( AdminCompanyDetail, 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(c)), None => Err((StatusCode::NOT_FOUND, "Company not found".to_string())), } } async fn approve_company( _auth: AuthUser, State(state): State, Path(id): Path, Json(_payload): Json, ) -> Result { let company = sqlx::query!( "SELECT id, user_id, status FROM company_profiles WHERE id = $1", id ) .fetch_optional(&state.pool) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?; let company = match company { Some(c) => c, None => return Err((StatusCode::NOT_FOUND, "Company not found".to_string())), }; if company.status == "APPROVED" { return Err((StatusCode::BAD_REQUEST, "Company is already approved".to_string())); } sqlx::query!( "UPDATE company_profiles SET status = 'APPROVED', 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!({ "id": id, "status": "APPROVED", "message": "Company approved successfully" }))) } async fn reject_company( _auth: AuthUser, State(state): State, Path(id): Path, Json(payload): Json, ) -> Result { let reason = payload.reason.as_deref().unwrap_or("No reason provided"); let company = sqlx::query!( "SELECT id, user_id, status FROM company_profiles WHERE id = $1", id ) .fetch_optional(&state.pool) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?; let company = match company { Some(c) => c, None => return Err((StatusCode::NOT_FOUND, "Company not found".to_string())), }; if company.status == "REJECTED" { return Err((StatusCode::BAD_REQUEST, "Company is already rejected".to_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!({ "id": id, "status": "REJECTED", "reason": reason, "message": "Company rejected" }))) } async fn suspend_company( _auth: AuthUser, State(state): State, Path(id): Path, Json(payload): Json, ) -> Result { let reason = payload.reason.as_deref().unwrap_or("No reason provided"); let company = sqlx::query!( "SELECT id, user_id, status FROM company_profiles WHERE id = $1", id ) .fetch_optional(&state.pool) .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?; let company = match company { Some(c) => c, None => return Err((StatusCode::NOT_FOUND, "Company not found".to_string())), }; if company.status == "SUSPENDED" { return Err((StatusCode::BAD_REQUEST, "Company is already suspended".to_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!({ "id": id, "status": "SUSPENDED", "reason": reason, "message": "Company suspended" }))) }