From 308daf495911f4b359ac7ca928be83a633e37dee Mon Sep 17 00:00:00 2001 From: Ashwin Kumar Sivakumar Date: Tue, 14 Jul 2026 21:23:30 +0530 Subject: [PATCH] fix(verification): align document keys and dedupe role maps across handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit extract_documents() recognized a stale set of document keys that no longer matched what the frontend actually uploads (portfolio_ownership_proof, professional_certifications, qualification_proof, tax_document), so every non-COMPANY role's verification case was created with an empty documents array. Also extracts role_key_to_display/role_to_table into a shared role_meta module — verifications.rs and approvals.rs were each missing UGC_CONTENT_CREATOR from their inline copies, so that role's rejections and final approvals were silently no-ops. Co-Authored-By: Claude Sonnet 5 --- apps/users/src/handlers/approvals.rs | 54 ++++-------------------- apps/users/src/handlers/mod.rs | 1 + apps/users/src/handlers/profile.rs | 32 ++++---------- apps/users/src/handlers/role_meta.rs | 43 +++++++++++++++++++ apps/users/src/handlers/verifications.rs | 37 +++------------- 5 files changed, 66 insertions(+), 101 deletions(-) create mode 100644 apps/users/src/handlers/role_meta.rs diff --git a/apps/users/src/handlers/approvals.rs b/apps/users/src/handlers/approvals.rs index 1ffaaf2..44842db 100644 --- a/apps/users/src/handlers/approvals.rs +++ b/apps/users/src/handlers/approvals.rs @@ -17,6 +17,8 @@ use db::models::verification::VerificationRepository; use serde::Deserialize; use uuid::Uuid; +use super::role_meta::{role_key_to_display, role_to_table}; + pub fn router() -> Router { Router::new() .route("/", get(list_pending)) @@ -165,44 +167,15 @@ async fn list_pending( .into_response() } -fn role_key_to_display(role_key: &str) -> String { - match role_key.to_uppercase().as_str() { - "COMPANY" => "Company".to_string(), - "CUSTOMER" => "Customer".to_string(), - "JOB_SEEKER" | "JOBSEEKER" => "Job Seeker".to_string(), - "PHOTOGRAPHER" => "Photographer".to_string(), - "MAKEUP_ARTIST" => "Makeup Artist".to_string(), - "TUTOR" => "Tutor".to_string(), - "DEVELOPER" => "Developer".to_string(), - "VIDEO_EDITOR" => "Video Editor".to_string(), - "GRAPHIC_DESIGNER" => "Graphic Designer".to_string(), - "SOCIAL_MEDIA_MANAGER" => "Social Media Manager".to_string(), - "FITNESS_TRAINER" => "Fitness Trainer".to_string(), - "CATERING_SERVICES" => "Catering Services".to_string(), - _ => role_key.to_string(), - } -} - async fn activate_profile_after_final_approval( state: &AppState, user_id: Uuid, role_key: &str, ) -> Result<(), sqlx::Error> { let role_key = role_key.to_uppercase(); - let table = match role_key.as_str() { - "COMPANY" => "company_profiles", - "CUSTOMER" => "customer_profiles", - "JOB_SEEKER" | "JOBSEEKER" => "job_seeker_profiles", - "PHOTOGRAPHER" => "photographer_profiles", - "MAKEUP_ARTIST" => "makeup_artist_profiles", - "TUTOR" => "tutor_profiles", - "DEVELOPER" => "developer_profiles", - "VIDEO_EDITOR" => "video_editor_profiles", - "GRAPHIC_DESIGNER" => "graphic_designer_profiles", - "SOCIAL_MEDIA_MANAGER" => "social_media_manager_profiles", - "FITNESS_TRAINER" => "fitness_trainer_profiles", - "CATERING_SERVICES" => "catering_service_profiles", - _ => return Ok(()), + let table = match role_to_table(&role_key) { + Some(t) => t, + None => return Ok(()), }; let user_role_profile_id = match sqlx::query_scalar::<_, Uuid>( @@ -276,20 +249,9 @@ async fn reject_profile_after_final_approval( reason: Option<&str>, ) -> Result<(), sqlx::Error> { let role_key = role_key.to_uppercase(); - let table = match role_key.as_str() { - "COMPANY" => "company_profiles", - "CUSTOMER" => "customer_profiles", - "JOB_SEEKER" | "JOBSEEKER" => "job_seeker_profiles", - "PHOTOGRAPHER" => "photographer_profiles", - "MAKEUP_ARTIST" => "makeup_artist_profiles", - "TUTOR" => "tutor_profiles", - "DEVELOPER" => "developer_profiles", - "VIDEO_EDITOR" => "video_editor_profiles", - "GRAPHIC_DESIGNER" => "graphic_designer_profiles", - "SOCIAL_MEDIA_MANAGER" => "social_media_manager_profiles", - "FITNESS_TRAINER" => "fitness_trainer_profiles", - "CATERING_SERVICES" => "catering_service_profiles", - _ => return Ok(()), + let table = match role_to_table(&role_key) { + Some(t) => t, + None => return Ok(()), }; let user_role_profile_id = match sqlx::query_scalar::<_, Uuid>( diff --git a/apps/users/src/handlers/mod.rs b/apps/users/src/handlers/mod.rs index 2070ff2..4f6ff91 100644 --- a/apps/users/src/handlers/mod.rs +++ b/apps/users/src/handlers/mod.rs @@ -26,4 +26,5 @@ pub mod user_roles; pub mod external_roles; pub mod verifications; pub mod profile; +pub mod role_meta; pub mod settings; diff --git a/apps/users/src/handlers/profile.rs b/apps/users/src/handlers/profile.rs index 90a6f07..030b6fe 100644 --- a/apps/users/src/handlers/profile.rs +++ b/apps/users/src/handlers/profile.rs @@ -11,6 +11,8 @@ use db::models::{role::RoleRepository, user::UserRepository, verification::Verif use serde::Deserialize; use uuid::Uuid; +use super::role_meta::role_to_table; + // ── Routers ─────────────────────────────────────────────────────────────────── pub fn router() -> Router { @@ -52,24 +54,6 @@ pub struct SubmitInput { // ── Helpers ─────────────────────────────────────────────────────────────────── -fn role_to_table(role_key: &str) -> Option<&'static str> { - match role_key.to_uppercase().as_str() { - "PHOTOGRAPHER" => Some("photographer_profiles"), - "MAKEUP_ARTIST" => Some("makeup_artist_profiles"), - "TUTOR" => Some("tutor_profiles"), - "DEVELOPER" => Some("developer_profiles"), - "VIDEO_EDITOR" => Some("video_editor_profiles"), - "GRAPHIC_DESIGNER" => Some("graphic_designer_profiles"), - "SOCIAL_MEDIA_MANAGER" => Some("social_media_manager_profiles"), - "FITNESS_TRAINER" => Some("fitness_trainer_profiles"), - "CATERING_SERVICES" => Some("catering_service_profiles"), - "UGC_CONTENT_CREATOR" => Some("ugc_content_creator_profiles"), - "JOB_SEEKER" | "JOBSEEKER" => Some("job_seeker_profiles"), - "CUSTOMER" => Some("customer_profiles"), - _ => None, - } -} - fn is_dummy_account_email(email: &str) -> bool { email.ends_with("@demo.com") || email == "paymentgateway@demo.com" @@ -78,17 +62,19 @@ fn is_dummy_account_email(email: &str) -> bool { } fn extract_documents(profile_data: &serde_json::Value) -> serde_json::Value { + // Keep in sync with DOC_FIELDS in nxtgauge-frontend-solid/src/lib/profile-fields-config.ts — + // each entry there is the source of truth for the document key a role's upload writes. let doc_keys = [ "aadhar_doc", "registration_doc", "gst_doc", - "sample_work", - "degree_certificate", + "address_proof", + "portfolio_ownership_proof", + "professional_certifications", + "qualification_proof", "certification_doc", "fssai_license", - "identity_proof", - "address_proof", - "portfolio_proof", + "tax_document", ]; let mut docs = vec![]; for key in &doc_keys { diff --git a/apps/users/src/handlers/role_meta.rs b/apps/users/src/handlers/role_meta.rs new file mode 100644 index 0000000..1533679 --- /dev/null +++ b/apps/users/src/handlers/role_meta.rs @@ -0,0 +1,43 @@ +//! Single source of truth for role_key <-> display name / profile table mappings, +//! shared by profile.rs, verifications.rs and approvals.rs so a role added to one +//! consumer can't silently be missing from another. + +pub fn role_key_to_display(role_key: &str) -> String { + match role_key.to_uppercase().as_str() { + "COMPANY" => "Company".to_string(), + "CUSTOMER" => "Customer".to_string(), + "JOB_SEEKER" | "JOBSEEKER" => "Job Seeker".to_string(), + "PHOTOGRAPHER" => "Photographer".to_string(), + "MAKEUP_ARTIST" => "Makeup Artist".to_string(), + "TUTOR" => "Tutor".to_string(), + "DEVELOPER" => "Developer".to_string(), + "VIDEO_EDITOR" => "Video Editor".to_string(), + "GRAPHIC_DESIGNER" => "Graphic Designer".to_string(), + "SOCIAL_MEDIA_MANAGER" => "Social Media Manager".to_string(), + "FITNESS_TRAINER" => "Fitness Trainer".to_string(), + "CATERING_SERVICES" => "Catering Services".to_string(), + "UGC_CONTENT_CREATOR" => "UGC Content Creator".to_string(), + _ => role_key.to_string(), + } +} + +/// Returns the profile table for a role, or None for roles with no dedicated +/// profile table under this pipeline (e.g. COMPANY, which is handled separately). +pub fn role_to_table(role_key: &str) -> Option<&'static str> { + match role_key.to_uppercase().as_str() { + "PHOTOGRAPHER" => Some("photographer_profiles"), + "MAKEUP_ARTIST" => Some("makeup_artist_profiles"), + "TUTOR" => Some("tutor_profiles"), + "DEVELOPER" => Some("developer_profiles"), + "VIDEO_EDITOR" => Some("video_editor_profiles"), + "GRAPHIC_DESIGNER" => Some("graphic_designer_profiles"), + "SOCIAL_MEDIA_MANAGER" => Some("social_media_manager_profiles"), + "FITNESS_TRAINER" => Some("fitness_trainer_profiles"), + "CATERING_SERVICES" => Some("catering_service_profiles"), + "UGC_CONTENT_CREATOR" => Some("ugc_content_creator_profiles"), + "JOB_SEEKER" | "JOBSEEKER" => Some("job_seeker_profiles"), + "CUSTOMER" => Some("customer_profiles"), + "COMPANY" => Some("company_profiles"), + _ => None, + } +} diff --git a/apps/users/src/handlers/verifications.rs b/apps/users/src/handlers/verifications.rs index d626af7..61f575f 100644 --- a/apps/users/src/handlers/verifications.rs +++ b/apps/users/src/handlers/verifications.rs @@ -11,6 +11,8 @@ use db::models::verification::{VerificationRepository}; use serde::Deserialize; use uuid::Uuid; +use super::role_meta::{role_key_to_display, role_to_table}; + /// Creates an entry in approval_requests after verification is approved. /// This is the bridge between Verification Management and Approval Management. async fn create_approval_request_from_verification( @@ -128,24 +130,6 @@ pub struct ActionPayload { pub reason: Option, } -fn role_key_to_display(role_key: &str) -> String { - match role_key.to_uppercase().as_str() { - "COMPANY" => "Company".to_string(), - "CUSTOMER" => "Customer".to_string(), - "JOB_SEEKER" | "JOBSEEKER" => "Job Seeker".to_string(), - "PHOTOGRAPHER" => "Photographer".to_string(), - "MAKEUP_ARTIST" => "Makeup Artist".to_string(), - "TUTOR" => "Tutor".to_string(), - "DEVELOPER" => "Developer".to_string(), - "VIDEO_EDITOR" => "Video Editor".to_string(), - "GRAPHIC_DESIGNER" => "Graphic Designer".to_string(), - "SOCIAL_MEDIA_MANAGER" => "Social Media Manager".to_string(), - "FITNESS_TRAINER" => "Fitness Trainer".to_string(), - "CATERING_SERVICES" => "Catering Services".to_string(), - _ => role_key.to_string(), - } -} - async fn trigger_rejection( state: &AppState, user_id: Uuid, @@ -157,20 +141,9 @@ async fn trigger_rejection( let reason_str = reason.unwrap_or("Verification rejected"); if case_type == "PROFILE_VERIFICATION" { - let table = match role_key.as_str() { - "COMPANY" => "company_profiles", - "CUSTOMER" => "customer_profiles", - "JOB_SEEKER" | "JOBSEEKER" => "job_seeker_profiles", - "PHOTOGRAPHER" => "photographer_profiles", - "MAKEUP_ARTIST" => "makeup_artist_profiles", - "TUTOR" => "tutor_profiles", - "DEVELOPER" => "developer_profiles", - "VIDEO_EDITOR" => "video_editor_profiles", - "GRAPHIC_DESIGNER" => "graphic_designer_profiles", - "SOCIAL_MEDIA_MANAGER" => "social_media_manager_profiles", - "FITNESS_TRAINER" => "fitness_trainer_profiles", - "CATERING_SERVICES" => "catering_service_profiles", - _ => return Ok(()), + let table = match role_to_table(&role_key) { + Some(t) => t, + None => return Ok(()), }; let user_role_profile_id = match sqlx::query_scalar::<_, Uuid>(