package com.example.ui.screens import android.content.ClipData import android.content.ClipboardManager import android.content.Context import android.widget.Toast import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.CheckCircle import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.HourglassEmpty import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.UploadFile import androidx.compose.material.icons.filled.Verified import androidx.compose.material.icons.filled.Warning import androidx.compose.material3.AlertDialog import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Surface import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.example.data.engine.ContactImportValidator import com.example.data.local.entity.ContactEntity import com.example.ui.components.StatusBadge import com.example.ui.theme.EmeraldDark import com.example.ui.theme.EmeraldLight import com.example.ui.theme.EmeraldPrimary import com.example.ui.viewmodel.MarketingViewModel import com.example.ui.viewmodel.UserRole @Composable fun ContactsScreen( viewModel: MarketingViewModel, modifier: Modifier = Modifier ) { val context = LocalContext.current val contacts by viewModel.contacts.collectAsStateWithLifecycle() val importReport by viewModel.importReport.collectAsStateWithLifecycle() val currentRole by viewModel.currentRole.collectAsStateWithLifecycle() var searchQuery by remember { mutableStateOf("") } var selectedTab by remember { mutableIntStateOf(0) } // 0: All, 1: Opted-In (Eligible), 2: Pending Approval, 3: Suppressed var showUploadDialog by remember { mutableStateOf(false) } var showAddDialog by remember { mutableStateOf(false) } var showTemplateDialog by remember { mutableStateOf(false) } var selectedContactForDetail by remember { mutableStateOf(null) } val filteredContacts = contacts.filter { contact -> val matchesSearch = searchQuery.isBlank() || contact.customerName.contains(searchQuery, ignoreCase = true) || contact.mobileNumber.contains(searchQuery) || contact.district.contains(searchQuery, ignoreCase = true) || contact.city.contains(searchQuery, ignoreCase = true) || contact.state.contains(searchQuery, ignoreCase = true) || contact.pinCode.contains(searchQuery) val matchesTab = when (selectedTab) { 1 -> contact.optInStatus == "OPTED_IN" && contact.isApprovedByAdmin 2 -> !contact.isApprovedByAdmin 3 -> contact.optInStatus == "OPTED_OUT" else -> true } matchesSearch && matchesTab } Box(modifier = modifier.fillMaxSize()) { Column( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp) ) { Spacer(modifier = Modifier.height(8.dp)) // Information banner: Consumer List (CSV Uploaded) Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = EmeraldPrimary.copy(alpha = 0.08f)), shape = RoundedCornerShape(10.dp) ) { Row( modifier = Modifier.padding(10.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = Icons.Default.Info, contentDescription = null, tint = EmeraldPrimary, modifier = Modifier.size(18.dp) ) Spacer(modifier = Modifier.width(8.dp)) Text( text = "Consumer Contact List (CSV Upload) — Messages are sent strictly to Consumer contacts uploaded via CSV (Excludes Vendors & Retailers).", fontSize = 11.sp, color = EmeraldDark, fontWeight = FontWeight.Medium ) } } Spacer(modifier = Modifier.height(8.dp)) // Action Bar: Template download & Upload CSV button Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { OutlinedButton( onClick = { showTemplateDialog = true }, modifier = Modifier.weight(1f), shape = RoundedCornerShape(10.dp) ) { Icon( imageVector = Icons.Default.Download, contentDescription = "CSV Template", modifier = Modifier.size(16.dp) ) Spacer(modifier = Modifier.width(6.dp)) Text("CSV Template", fontSize = 12.sp) } Button( onClick = { showUploadDialog = true }, modifier = Modifier .weight(1f) .testTag("upload_contacts_button"), shape = RoundedCornerShape(10.dp), colors = ButtonDefaults.buttonColors(containerColor = EmeraldPrimary) ) { Icon( imageVector = Icons.Default.UploadFile, contentDescription = "Upload CSV", modifier = Modifier.size(16.dp) ) Spacer(modifier = Modifier.width(6.dp)) Text("Import Consumer CSV", fontSize = 12.sp) } } Spacer(modifier = Modifier.height(10.dp)) // Search Bar OutlinedTextField( value = searchQuery, onValueChange = { searchQuery = it }, placeholder = { Text("Search Consumer name, mobile, district, PIN...") }, leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) }, modifier = Modifier .fillMaxWidth() .testTag("contact_search_input"), shape = RoundedCornerShape(12.dp), singleLine = true ) Spacer(modifier = Modifier.height(10.dp)) // Status Tabs TabRow( selectedTabIndex = selectedTab, containerColor = Color.Transparent, contentColor = EmeraldPrimary ) { Tab( selected = selectedTab == 0, onClick = { selectedTab = 0 }, text = { Text("All (${contacts.size})", fontSize = 12.sp) } ) Tab( selected = selectedTab == 1, onClick = { selectedTab = 1 }, text = { Text("Opted-In", fontSize = 12.sp) } ) Tab( selected = selectedTab == 2, onClick = { selectedTab = 2 }, text = { Text("Pending Review", fontSize = 12.sp) } ) Tab( selected = selectedTab == 3, onClick = { selectedTab = 3 }, text = { Text("Suppressed", fontSize = 12.sp) } ) } Spacer(modifier = Modifier.height(8.dp)) // Admin Bulk Approval Banner if there are pending contacts val pendingCount = contacts.count { !it.isApprovedByAdmin } if (pendingCount > 0 && currentRole != UserRole.CONSUMER) { Card( modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(10.dp), colors = CardDefaults.cardColors(containerColor = Color(0xFFFEF3C7)) ) { Row( modifier = Modifier.padding(10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f) ) { Icon(Icons.Default.Warning, contentDescription = null, tint = Color(0xFF92400E)) Spacer(modifier = Modifier.width(8.dp)) Text( text = "$pendingCount contacts pending Admin review", fontSize = 12.sp, color = Color(0xFF92400E), fontWeight = FontWeight.Medium ) } Button( onClick = { viewModel.approveAllPendingContacts() }, colors = ButtonDefaults.buttonColors(containerColor = EmeraldPrimary), shape = RoundedCornerShape(8.dp) ) { Text("Approve All", fontSize = 11.sp) } } } Spacer(modifier = Modifier.height(8.dp)) } // Contacts List if (filteredContacts.isEmpty()) { Box( modifier = Modifier .fillMaxSize() .weight(1f), contentAlignment = Alignment.Center ) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Icon( imageVector = Icons.Default.Person, contentDescription = null, tint = Color.Gray, modifier = Modifier.size(48.dp) ) Spacer(modifier = Modifier.height(8.dp)) Text( text = "No contacts found matching filter", fontSize = 14.sp, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } } else { LazyColumn( modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(10.dp) ) { items(filteredContacts) { contact -> ContactItemCard( contact = contact, currentRole = currentRole, onClick = { selectedContactForDetail = contact }, onApprove = { viewModel.approveContact(contact.id) } ) } item { Spacer(modifier = Modifier.height(72.dp)) } } } } // Floating Action Button to Add Single Contact FloatingActionButton( onClick = { showAddDialog = true }, modifier = Modifier .align(Alignment.BottomEnd) .padding(20.dp) .testTag("add_contact_fab"), containerColor = EmeraldPrimary, contentColor = Color.White ) { Icon(Icons.Default.Add, contentDescription = "Add Contact") } // Dialogs if (showUploadDialog) { CsvUploadModal( onDismiss = { showUploadDialog = false }, onUpload = { csvText -> viewModel.importCsv( csvText = csvText, defaultCategoryName = "Consumer Upload List", defaultCategoryId = 1L ) showUploadDialog = false } ) } if (showTemplateDialog) { CsvTemplateDialog( onDismiss = { showTemplateDialog = false }, onCopy = { val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager clipboard.setPrimaryClip(ClipData.newPlainText("CSV Template", ContactImportValidator.SAMPLE_CSV_TEMPLATE)) Toast.makeText(context, "Official CSV Template copied to clipboard!", Toast.LENGTH_SHORT).show() showTemplateDialog = false } ) } if (showAddDialog) { AddContactDialog( onDismiss = { showAddDialog = false }, onSave = { newContact -> viewModel.importCsv( csvText = "country_code,mobile_number,customer_name,district,state,pin_code,whatsapp_opt_in,consent_source,consent_date,customer_notes\n" + "${newContact.countryCode},${newContact.mobileNumber},${newContact.customerName},${newContact.district},${newContact.state},${newContact.pinCode},true,Manual In-App Entry,2026-10-16,${newContact.customerNotes}", defaultCategoryName = "Consumer Upload List", defaultCategoryId = 1L ) showAddDialog = false } ) } // Import Validation Report Modal importReport?.let { report -> ImportReportDialog( report = report, onDismiss = { viewModel.clearImportReport() } ) } // Contact Details & Consent Record Dialog selectedContactForDetail?.let { contact -> ContactDetailConsentDialog( contact = contact, onDismiss = { selectedContactForDetail = null }, onToggleOptIn = { isOptedIn -> viewModel.updateContactOptIn(contact.id, isOptedIn) selectedContactForDetail = contact.copy(optInStatus = if (isOptedIn) "OPTED_IN" else "OPTED_OUT") }, onDelete = { viewModel.deleteContact(contact.id) selectedContactForDetail = null } ) } } } /** * ContactItemCard implements the exact display requirement: * "No Need to show Contact Category or User type show in Contacts Details * SHow Only(Consumer Name/Mobile Number and District, State PIN Code) else show Mobile Number and District Name" */ @Composable fun ContactItemCard( contact: ContactEntity, currentRole: UserRole, onClick: () -> Unit = {}, onApprove: () -> Unit ) { Card( modifier = Modifier .fillMaxWidth() .clickable { onClick() }, shape = RoundedCornerShape(12.dp), colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), elevation = CardDefaults.cardElevation(defaultElevation = 1.dp) ) { Column(modifier = Modifier.padding(14.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.Top ) { Column(modifier = Modifier.weight(1f)) { val hasConsumerName = contact.customerName.isNotBlank() if (hasConsumerName) { // Show Only: (Consumer Name / Mobile Number and District, State PIN Code) Text( text = contact.customerName, fontWeight = FontWeight.Bold, fontSize = 15.sp, color = MaterialTheme.colorScheme.onSurface ) Spacer(modifier = Modifier.height(2.dp)) Text( text = "Mobile: ${contact.e164Phone}", fontSize = 13.sp, fontFamily = FontFamily.Monospace, color = EmeraldPrimary, fontWeight = FontWeight.SemiBold ) val districtVal = contact.district.ifBlank { contact.city } val locList = listOf(districtVal, contact.state, contact.pinCode).filter { it.isNotBlank() } if (locList.isNotEmpty()) { Spacer(modifier = Modifier.height(2.dp)) Text( text = "District, State PIN Code: ${locList.joinToString(", ")}", fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } else { // Else show Mobile Number and District Name Text( text = "Mobile: ${contact.e164Phone}", fontSize = 14.sp, fontFamily = FontFamily.Monospace, color = EmeraldPrimary, fontWeight = FontWeight.Bold ) val districtVal = contact.district.ifBlank { contact.city } if (districtVal.isNotBlank()) { Spacer(modifier = Modifier.height(2.dp)) Text( text = "District: $districtVal", fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } } Column(horizontalAlignment = Alignment.End) { StatusBadge(status = contact.optInStatus) if (!contact.isApprovedByAdmin) { Spacer(modifier = Modifier.height(4.dp)) Text( text = "Awaiting Admin Review", fontSize = 10.sp, color = Color(0xFFD97706), fontWeight = FontWeight.Bold ) } } } if (!contact.isApprovedByAdmin && currentRole != UserRole.CONSUMER) { Spacer(modifier = Modifier.height(10.dp)) Button( onClick = onApprove, modifier = Modifier.fillMaxWidth(), shape = RoundedCornerShape(8.dp), colors = ButtonDefaults.buttonColors(containerColor = EmeraldPrimary) ) { Icon(Icons.Default.Check, contentDescription = null, modifier = Modifier.size(14.dp)) Spacer(modifier = Modifier.width(6.dp)) Text("Admin Approve for Broadcasts", fontSize = 11.sp) } } } } } @Composable fun CsvUploadModal( onDismiss: () -> Unit, onUpload: (csvText: String) -> Unit ) { var csvText by remember { mutableStateOf(ContactImportValidator.SAMPLE_CSV_TEMPLATE) } AlertDialog( onDismissRequest = onDismiss, title = { Text("Import Consumer Contact List (CSV)") }, text = { Column(modifier = Modifier.fillMaxWidth()) { Text( text = "Upload Consumer Contacts using the official spreadsheet template. Messages will be sent exclusively to Consumer contacts uploaded via CSV (Excludes Vendors and Retailers).", fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant ) Spacer(modifier = Modifier.height(10.dp)) Text("CSV File Content (or paste spreadsheet rows):", fontSize = 12.sp, fontWeight = FontWeight.SemiBold) Spacer(modifier = Modifier.height(4.dp)) OutlinedTextField( value = csvText, onValueChange = { csvText = it }, modifier = Modifier .fillMaxWidth() .height(200.dp), textStyle = androidx.compose.ui.text.TextStyle( fontFamily = FontFamily.Monospace, fontSize = 11.sp ) ) } }, confirmButton = { Button( onClick = { onUpload(csvText) }, colors = ButtonDefaults.buttonColors(containerColor = EmeraldPrimary) ) { Text("Validate & Import Consumers") } }, dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } } ) } @Composable fun CsvTemplateDialog( onDismiss: () -> Unit, onCopy: () -> Unit ) { AlertDialog( onDismissRequest = onDismiss, title = { Text("Consumer Contact CSV Template") }, text = { Column(modifier = Modifier.fillMaxWidth()) { Text( text = "Meta WhatsApp Business Platform requires verifiable marketing opt-in before bulk messaging. Below is the official Consumer spreadsheet schema:", fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant ) Spacer(modifier = Modifier.height(8.dp)) Surface( color = Color(0xFFF1F5F9), shape = RoundedCornerShape(8.dp), modifier = Modifier.fillMaxWidth() ) { Text( text = "Columns:\n1. country_code (e.g. 91)\n2. mobile_number (10 digits)\n3. customer_name\n4. district\n5. state\n6. pin_code\n7. whatsapp_opt_in (true/yes)\n8. consent_source (e.g. Web Form)\n9. consent_date (YYYY-MM-DD)\n10. customer_notes", fontSize = 11.sp, fontFamily = FontFamily.Monospace, modifier = Modifier.padding(10.dp) ) } } }, confirmButton = { Button( onClick = onCopy, colors = ButtonDefaults.buttonColors(containerColor = EmeraldPrimary) ) { Icon(Icons.Default.ContentCopy, contentDescription = null, modifier = Modifier.size(16.dp)) Spacer(modifier = Modifier.width(6.dp)) Text("Copy CSV Template") } }, dismissButton = { TextButton(onClick = onDismiss) { Text("Close") } } ) } @Composable fun ImportReportDialog( report: com.example.data.engine.ImportValidationReport, onDismiss: () -> Unit ) { AlertDialog( onDismissRequest = onDismiss, title = { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.Verified, contentDescription = null, tint = EmeraldPrimary) Spacer(modifier = Modifier.width(8.dp)) Text("Import Validation Report") } }, text = { Column(modifier = Modifier.fillMaxWidth()) { Text( text = "Processed ${report.totalRowsProcessed} total records from Consumer CSV:", fontSize = 13.sp, fontWeight = FontWeight.SemiBold ) Spacer(modifier = Modifier.height(10.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Text("• Valid Opted-In Consumers:", fontSize = 12.sp) Text("${report.validCount}", fontWeight = FontWeight.Bold, color = EmeraldPrimary, fontSize = 12.sp) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Text("• Duplicate Numbers Detected:", fontSize = 12.sp) Text("${report.duplicateCount}", fontWeight = FontWeight.Bold, color = Color(0xFFD97706), fontSize = 12.sp) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Text("• Non-Consenting / Missing Opt-in:", fontSize = 12.sp) Text("${report.nonConsentingCount}", fontWeight = FontWeight.Bold, color = Color(0xFFE11D48), fontSize = 12.sp) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Text("• Invalid Rows / Format Errors:", fontSize = 12.sp) Text("${report.invalidRowsCount}", fontWeight = FontWeight.Bold, color = Color.Gray, fontSize = 12.sp) } Spacer(modifier = Modifier.height(12.dp)) Text( text = "Note: In compliance with Meta Business terms, records with missing opt-in were automatically suppressed from campaign queues.", fontSize = 11.sp, color = MaterialTheme.colorScheme.onSurfaceVariant ) } }, confirmButton = { Button( onClick = onDismiss, colors = ButtonDefaults.buttonColors(containerColor = EmeraldPrimary) ) { Text("Understood") } } ) } @Composable fun AddContactDialog( onDismiss: () -> Unit, onSave: (ContactEntity) -> Unit ) { var name by remember { mutableStateOf("") } var phone by remember { mutableStateOf("") } var district by remember { mutableStateOf("") } var state by remember { mutableStateOf("") } var pinCode by remember { mutableStateOf("") } AlertDialog( onDismissRequest = onDismiss, title = { Text("Add Consumer Contact") }, text = { Column( modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp) ) { OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Consumer Name") }, singleLine = true, modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = phone, onValueChange = { phone = it }, label = { Text("Mobile Number (10 digits) *") }, singleLine = true, modifier = Modifier.fillMaxWidth() ) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { OutlinedTextField( value = district, onValueChange = { district = it }, label = { Text("District") }, singleLine = true, modifier = Modifier.weight(1f) ) OutlinedTextField( value = state, onValueChange = { state = it }, label = { Text("State") }, singleLine = true, modifier = Modifier.weight(1f) ) } OutlinedTextField( value = pinCode, onValueChange = { pinCode = it }, label = { Text("PIN Code") }, singleLine = true, modifier = Modifier.fillMaxWidth() ) } }, confirmButton = { Button( onClick = { if (phone.isNotBlank()) { onSave( ContactEntity( countryCode = "91", mobileNumber = phone.takeLast(10), e164Phone = "+91${phone.takeLast(10)}", customerName = name.trim(), district = district.trim(), city = district.trim(), state = state.trim(), pinCode = pinCode.trim(), categoryName = "Consumer Upload List", categoryId = 1L, optInStatus = "OPTED_IN", consentSource = "Manual Entry", consentDate = "2026-10-16", isApprovedByAdmin = false, isConsumerUpload = true ) ) } }, colors = ButtonDefaults.buttonColors(containerColor = EmeraldPrimary) ) { Text("Save Consumer") } }, dismissButton = { TextButton(onClick = onDismiss) { Text("Cancel") } } ) } /** * Screen 8: Contact Details and Consent Record * Strict requirement: * "No Need to show Contact Category or User type show in Contacts Details * SHow Only(Consumer Name/Mobile Number and District, State PIN Code) else show Mobile Number and District Name" */ @Composable fun ContactDetailConsentDialog( contact: ContactEntity, onDismiss: () -> Unit, onToggleOptIn: (Boolean) -> Unit, onDelete: () -> Unit ) { val hasConsumerName = contact.customerName.isNotBlank() val isOptedIn = contact.optInStatus == "OPTED_IN" AlertDialog( onDismissRequest = onDismiss, title = { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.Verified, contentDescription = null, tint = EmeraldPrimary) Spacer(modifier = Modifier.width(8.dp)) Text("Contact Details & Consent", fontSize = 16.sp, fontWeight = FontWeight.Bold) } }, text = { Column( modifier = Modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp) ) { // Formatted Contact Details: (Consumer Name / Mobile Number and District, State PIN Code) else (Mobile Number and District Name) Surface( color = Color(0xFFF8FAFC), shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) { Column(modifier = Modifier.padding(12.dp)) { if (hasConsumerName) { Text( text = contact.customerName, fontSize = 16.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurface ) Spacer(modifier = Modifier.height(3.dp)) Text( text = "Mobile Number: ${contact.e164Phone}", fontSize = 13.sp, fontFamily = FontFamily.Monospace, color = EmeraldPrimary, fontWeight = FontWeight.SemiBold ) val districtVal = contact.district.ifBlank { contact.city } val locList = listOf(districtVal, contact.state, contact.pinCode).filter { it.isNotBlank() } if (locList.isNotEmpty()) { Spacer(modifier = Modifier.height(3.dp)) Text( text = "District, State PIN Code: ${locList.joinToString(", ")}", fontSize = 12.sp, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } else { Text( text = "Mobile Number: ${contact.e164Phone}", fontSize = 15.sp, fontFamily = FontFamily.Monospace, color = EmeraldPrimary, fontWeight = FontWeight.Bold ) val districtVal = contact.district.ifBlank { contact.city } if (districtVal.isNotBlank()) { Spacer(modifier = Modifier.height(3.dp)) Text( text = "District Name: $districtVal", fontSize = 13.sp, color = MaterialTheme.colorScheme.onSurfaceVariant ) } } } } // Consent & Compliance Audit Record Surface( color = if (isOptedIn) Color(0xFFF0FDF4) else Color(0xFFFEF2F2), shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) { Column(modifier = Modifier.padding(12.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text("Consent Audit Status:", fontSize = 12.sp, fontWeight = FontWeight.SemiBold) StatusBadge(status = contact.optInStatus) } Spacer(modifier = Modifier.height(6.dp)) Text("• Consent Source: ${contact.consentSource}", fontSize = 11.sp) Text("• Opt-in Timestamp: ${contact.consentDate}", fontSize = 11.sp) Text("• Admin Review: ${if (contact.isApprovedByAdmin) "Approved for WhatsApp Broadcast" else "Pending Review"}", fontSize = 11.sp) Text("• Message Target: Consumer List (CSV)", fontSize = 11.sp) if (contact.customerNotes.isNotBlank()) { Text("• Notes: ${contact.customerNotes}", fontSize = 11.sp, color = Color.Gray) } } } // Action controls for consent management and deletion Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { OutlinedButton( onClick = { onToggleOptIn(!isOptedIn) }, modifier = Modifier.weight(1f), shape = RoundedCornerShape(8.dp) ) { Text(if (isOptedIn) "Opt-Out / Suppress" else "Grant Opt-In", fontSize = 11.sp) } OutlinedButton( onClick = onDelete, modifier = Modifier.weight(1f), colors = ButtonDefaults.outlinedButtonColors(contentColor = Color(0xFFE11D48)), shape = RoundedCornerShape(8.dp) ) { Icon(Icons.Default.Delete, contentDescription = null, modifier = Modifier.size(14.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Delete", fontSize = 11.sp) } } } }, confirmButton = { Button( onClick = onDismiss, colors = ButtonDefaults.buttonColors(containerColor = EmeraldPrimary) ) { Text("Close") } } ) }