package com.example.ui.screens import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* 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.testTag import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import com.example.data.BookingEntity import com.example.data.ModelProfile import com.example.data.TransactionLog import com.example.data.UserWallet import com.example.data.CashDepositRequest import com.example.data.WithdrawalEntity import com.example.ui.PortalViewModel import com.example.ui.theme.* import java.text.SimpleDateFormat import java.util.* @Composable fun AdminPortalView( viewModel: PortalViewModel, userWallets: List, models: List, bookings: List, logs: List ) { var selectedTab by remember { mutableStateOf("overview") } // "overview", "models", "users", "bookings", "reports" Scaffold( bottomBar = { NavigationBar( containerColor = DarkGreyGlass, modifier = Modifier.windowInsetsPadding(WindowInsets.navigationBars).testTag("admin_bottom_nav") ) { NavigationBarItem( selected = selectedTab == "overview", onClick = { selectedTab = "overview" }, icon = { Icon(Icons.Default.Dashboard, contentDescription = "Overview") }, label = { Text("Overview") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) NavigationBarItem( selected = selectedTab == "models", onClick = { selectedTab = "models" }, icon = { Icon(Icons.Default.SupervisedUserCircle, contentDescription = "Models") }, label = { Text("Models") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) NavigationBarItem( selected = selectedTab == "users", onClick = { selectedTab = "users" }, icon = { Icon(Icons.Default.Group, contentDescription = "Users") }, label = { Text("Users") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) NavigationBarItem( selected = selectedTab == "agents", onClick = { selectedTab = "agents" }, icon = { Icon(Icons.Default.LocalAtm, contentDescription = "Agents") }, label = { Text("Agents") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) NavigationBarItem( selected = selectedTab == "reports", onClick = { selectedTab = "reports" }, icon = { Icon(Icons.Default.Assessment, contentDescription = "Reports") }, label = { Text("Reports") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) } }, containerColor = Obsidian ) { paddingValues -> Box( modifier = Modifier .fillMaxSize() .padding(paddingValues) ) { when (selectedTab) { "overview" -> AdminOverviewTab( viewModel = viewModel, userWallets = userWallets, models = models, bookings = bookings ) "models" -> AdminModelsTab( viewModel = viewModel, models = models ) "users" -> AdminUsersTab( viewModel = viewModel, userWallets = userWallets ) "agents" -> AdminAgentsTab( viewModel = viewModel, userWallets = userWallets ) "reports" -> AdminReportsTab( viewModel = viewModel, logs = logs, bookings = bookings, userWallets = userWallets, models = models ) } } } } @Composable fun AdminOverviewTab( viewModel: PortalViewModel, userWallets: List, models: List, bookings: List ) { var commRateInput by remember { mutableStateOf(viewModel.liveCommissionPercent.toString()) } var exchRateInput by remember { mutableStateOf(viewModel.liveExchangeRate.toString()) } var splitRateInput by remember { mutableStateOf(viewModel.liveAgentSplitPercent.toString()) } // Dynamic calculations representing the real, live synchronized database! val totalUsers = userWallets.size val totalModels = models.size val totalBookings = bookings.size // Settle dynamic agency revenue (15% platform commission cut of all "COMPLETED" transactions) val completedBookings = bookings.filter { it.status == "COMPLETED" } val totalRevenue = completedBookings.sumOf { it.totalPrice * 0.15 } val vipPlanCount = models.filter { it.membershipStatus == "VIP Plan" }.size + userWallets.filter { it.membershipStatus == "VIP Membership" }.size val premiumPlanCount = models.filter { it.membershipStatus == "Premium Plan" }.size + userWallets.filter { it.membershipStatus == "Premium Membership" }.size val activeMemberships = vipPlanCount + premiumPlanCount LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { item { Spacer(modifier = Modifier.height(12.dp)) val adminWallet by viewModel.adminWalletFlow.collectAsState(initial = null) Card( modifier = Modifier.fillMaxWidth().testTag("admin_treasury_card"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column(modifier = Modifier.padding(20.dp)) { Text("SaaS Sovereign Admin Treasury Balance (20% Escrow Splits)", color = TextSecondary, style = MaterialTheme.typography.labelSmall) Text( String.format(Locale.US, "$%,.2f", adminWallet?.balance ?: 0.0), color = CyanAccent, style = MaterialTheme.typography.displayMedium, fontWeight = FontWeight.ExtraBold ) Spacer(modifier = Modifier.height(6.dp)) Text( "Showing real-time administrative wallet holdings from completed booking proof audits. (Sovereign Cumulative Revenue: $${String.format(Locale.US, "%,.2f", adminWallet?.totalEarnings ?: 0.0)})", color = TextSecondary, style = MaterialTheme.typography.bodySmall ) } } } item { Text("General Command Center Metrics", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) } item { Row( horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth() ) { StatCard( modifier = Modifier.weight(1f), title = "Total Models", value = totalModels.toString(), icon = Icons.Default.Groups, color = BrandGold ) StatCard( modifier = Modifier.weight(1f), title = "Total Users", value = totalUsers.toString(), icon = Icons.Default.Person, color = CyanAccent ) } } item { Row( horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth() ) { StatCard( modifier = Modifier.weight(1f), title = "Total Bookings", value = totalBookings.toString(), icon = Icons.Default.Book, color = BrandGold ) StatCard( modifier = Modifier.weight(1f), title = "VIP Subscribed", value = activeMemberships.toString(), icon = Icons.Default.Stars, color = CyanAccent ) } } // 🔍 ESCROW COMPLIANCE & KYC VERIFICATION AUDIT CENTRE item { Text("SaaS Escrow Compliance & KYC Audit Centre", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) } item { Card( modifier = Modifier.fillMaxWidth().testTag("admin_kyc_audit_card"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), border = BorderStroke(1.dp, BrandGold.copy(0.3f)) ) { Column(modifier = Modifier.padding(16.dp)) { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.VerifiedUser, "compliance", tint = BrandGold, modifier = Modifier.size(22.dp)) Spacer(modifier = Modifier.width(8.dp)) Text("Active Identity & Document Audits", style = MaterialTheme.typography.titleSmall, color = TextPrimary, fontWeight = FontWeight.ExtraBold) } Text("Manually approve/reject NID and live selfie biometric matches to release booking permissions.", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Spacer(modifier = Modifier.height(14.dp)) HorizontalDivider(color = TextSecondary.copy(0.12f)) Spacer(modifier = Modifier.height(14.dp)) // Section 1: User Accounts Compliance Text("A. CLIENT USER ACCOUNTS QUEUE", style = MaterialTheme.typography.labelSmall, color = BrandGold, fontWeight = FontWeight.Bold) Spacer(modifier = Modifier.height(8.dp)) if (userWallets.isEmpty()) { Text("No registered client users.", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } else { userWallets.forEach { cw -> Card( modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Row( modifier = Modifier.fillMaxWidth().padding(12.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column(modifier = Modifier.weight(1f)) { Text(cw.name, style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Email: ${cw.email} | NID: ${if(cw.nidNumber.isEmpty()) "Not Provided" else cw.nidNumber}", style = MaterialTheme.typography.labelSmall, color = TextSecondary) Spacer(modifier = Modifier.height(4.dp)) Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) { StatusPill("EMAIL", cw.emailVerified) StatusPill("PHONE", cw.phoneVerified) KYCStatusPill("KYC", cw.kycStatus) } } Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { Button( onClick = { viewModel.setUserKYCStatus(cw.userId, "verified") }, colors = ButtonDefaults.buttonColors(containerColor = Color.Green, contentColor = Color.Black), shape = RoundedCornerShape(4.dp), contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp), modifier = Modifier.height(28.dp).testTag("admin_approve_user_${cw.userId}") ) { Text("Approve", fontSize = 10.sp, fontWeight = FontWeight.Bold) } Button( onClick = { viewModel.setUserKYCStatus(cw.userId, "rejected") }, colors = ButtonDefaults.buttonColors(containerColor = CoralRed, contentColor = Color.White), shape = RoundedCornerShape(4.dp), contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp), modifier = Modifier.height(28.dp).testTag("admin_reject_user_${cw.userId}") ) { Text("Reject", fontSize = 10.sp, fontWeight = FontWeight.Bold) } } } } } } Spacer(modifier = Modifier.height(14.dp)) HorizontalDivider(color = TextSecondary.copy(0.12f)) Spacer(modifier = Modifier.height(14.dp)) // Section 2: Model Accounts Compliance Text("B. COMPANIONS / MODELS QUEUE", style = MaterialTheme.typography.labelSmall, color = BrandGold, fontWeight = FontWeight.Bold) Spacer(modifier = Modifier.height(8.dp)) if (models.isEmpty()) { Text("No companions in directory.", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } else { models.forEach { m -> Card( modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Row( modifier = Modifier.fillMaxWidth().padding(12.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column(modifier = Modifier.weight(1f)) { Row(verticalAlignment = Alignment.CenterVertically) { Text(m.name, style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Spacer(modifier = Modifier.width(6.dp)) if (m.isVerified) { Icon(Icons.Default.CheckCircle, "verified", tint = Color.Green, modifier = Modifier.size(14.dp)) } } Text("Rate: ৳${String.format(Locale.US, "%,.0f", m.hourlyRate * 500)}/hr | NID: ${if(m.nidNumber.isEmpty()) "Not Provided" else m.nidNumber}", style = MaterialTheme.typography.labelSmall, color = TextSecondary) Spacer(modifier = Modifier.height(4.dp)) Row(horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically) { StatusPill("EMAIL", m.emailVerified) StatusPill("PHONE", m.phoneVerified) KYCStatusPill("KYC", m.kycStatus) } } Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { Button( onClick = { viewModel.setModelKYCStatus(m.id, "verified") }, colors = ButtonDefaults.buttonColors(containerColor = Color.Green, contentColor = Color.Black), shape = RoundedCornerShape(4.dp), contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp), modifier = Modifier.height(28.dp).testTag("admin_approve_model_${m.id}") ) { Text("Approve", fontSize = 10.sp, fontWeight = FontWeight.Bold) } Button( onClick = { viewModel.setModelKYCStatus(m.id, "rejected") }, colors = ButtonDefaults.buttonColors(containerColor = CoralRed, contentColor = Color.White), shape = RoundedCornerShape(4.dp), contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp), modifier = Modifier.height(28.dp).testTag("admin_reject_model_${m.id}") ) { Text("Reject", fontSize = 10.sp, fontWeight = FontWeight.Bold) } } } } } } Spacer(modifier = Modifier.height(14.dp)) HorizontalDivider(color = TextSecondary.copy(0.12f)) Spacer(modifier = Modifier.height(14.dp)) // Section 3: Job Completion Selfie Verification Queue Text("C. JOB COMPLETION SELFIE PROOFS QUEUE", style = MaterialTheme.typography.labelSmall, color = BrandGold, fontWeight = FontWeight.Bold) Spacer(modifier = Modifier.height(8.dp)) val pendingProofBookings = bookings.filter { it.status == "PROOF_UPLOADED" } if (pendingProofBookings.isEmpty()) { Text("No completed jobs waiting for selfie verification.", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } else { pendingProofBookings.forEach { pb -> Card( modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp).testTag("admin_proof_card_${pb.id}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Row( modifier = Modifier.fillMaxWidth().padding(12.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column(modifier = Modifier.weight(1f)) { Text("Booking Ref #${pb.id} | Model: ${pb.modelName}", style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Client User: ${pb.userName} | Amount: $${String.format(Locale.US, "%,.2f", pb.totalPrice)}", style = MaterialTheme.typography.labelSmall, color = TextSecondary) Spacer(modifier = Modifier.height(6.dp)) Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.CameraAlt, "proof_icon", tint = BrandGold, modifier = Modifier.size(13.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Uploaded Proof: proof_front_profile.png (Simulated Camera Biometric)", style = MaterialTheme.typography.bodySmall, color = CyanAccent, fontWeight = FontWeight.SemiBold) } } Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { Button( onClick = { viewModel.approveProof(pb.id) }, colors = ButtonDefaults.buttonColors(containerColor = Color.Green, contentColor = Color.Black), shape = RoundedCornerShape(4.dp), contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp), modifier = Modifier.height(28.dp).testTag("admin_approve_proof_${pb.id}") ) { Text("Approve / Release", fontSize = 10.sp, fontWeight = FontWeight.Bold) } Button( onClick = { viewModel.rejectProof(pb.id) }, colors = ButtonDefaults.buttonColors(containerColor = CoralRed, contentColor = Color.White), shape = RoundedCornerShape(4.dp), contentPadding = PaddingValues(horizontal = 8.dp, vertical = 2.dp), modifier = Modifier.height(28.dp).testTag("admin_reject_proof_${pb.id}") ) { Text("Reject", fontSize = 10.sp, fontWeight = FontWeight.Bold) } } } } } } } } } // ⚙️ SAAS GLOBAL RULES CONFIGURATOR item { Card( modifier = Modifier.fillMaxWidth().testTag("admin_saas_rules_card"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), border = BorderStroke(1.dp, BrandGold.copy(0.2f)) ) { Column(modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(14.dp)) { Text( text = "⚙️ SAAS GLOBAL RULES ENGINE", color = BrandGold, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.ExtraBold ) Text( text = "Fine-tune country-wide deposit commissions, sovereign ledger splits, and fiat exchange pricing variables dynamically.", color = TextSecondary, style = MaterialTheme.typography.bodySmall ) Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) { OutlinedTextField( value = exchRateInput, onValueChange = { exchRateInput = it }, label = { Text("Exchange Rate (USD ➔ BDT)") }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.weight(1f).testTag("setup_exchange_rate") ) OutlinedTextField( value = commRateInput, onValueChange = { commRateInput = it }, label = { Text("Net Escrow Fee %") }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.weight(1f).testTag("setup_commission_rate") ) } OutlinedTextField( value = splitRateInput, onValueChange = { splitRateInput = it }, label = { Text("Cash Agent Revenue Split Ratio %") }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth().testTag("setup_split_rate") ) Button( onClick = { val cr = commRateInput.toDoubleOrNull() ?: 5.0 val er = exchRateInput.toDoubleOrNull() ?: 118.5 val sr = splitRateInput.toDoubleOrNull() ?: 80.0 viewModel.setLiveCommissionPercentValue(cr) viewModel.setLiveExchangeRateValue(er) viewModel.setLiveAgentSplitPercentValue(sr) }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(8.dp), modifier = Modifier.fillMaxWidth().testTag("apply_global_rules_btn") ) { Text("Apply Operational Parameters", fontWeight = FontWeight.Bold) } } } } // 💳 DECENTRALIZED PAYMENT GATEWAYS COMMAND PANEL item { Card( modifier = Modifier.fillMaxWidth().testTag("admin_payment_gateways_card"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated) ) { Column(modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { Text( text = "💳 MULTI-CHANNEL INTENT GATEWAYS (500+ Nodes)", color = TextPrimary, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold ) Text( text = "Suspend or release operational payment gateways, direct banks, and cryptocurrency channels instantly.", color = TextSecondary, style = MaterialTheme.typography.bodySmall ) Spacer(modifier = Modifier.height(4.dp)) val gateways = listOf( "bKash Quick Pay" to "BDT Fiat Gateway", "Nagad Sovereign API" to "BDT Direct Settlement", "Rocket Teller" to "BDT Utility Ledger", "Binance Pay API" to "Multi-Crypto Node", "USDC Circle Safehouse" to "Stablecoin Smart Contract", "Mastercard SecureCode" to "Direct Acquiring Card", "Airtm P2P Transfer" to "Sovereign Cross-Border", "Stripe Gateway Engine" to "Global Card Rails" ) gateways.forEach { (gateway, typeDesc) -> val isSuspended = viewModel.disabledGateways.contains(gateway) Row( modifier = Modifier .fillMaxWidth() .background(DarkGreyGlass, RoundedCornerShape(8.dp)) .padding(12.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column { Text(gateway, style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Text(typeDesc, style = MaterialTheme.typography.labelSmall, color = TextSecondary) } Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp)) { Surface( color = if (isSuspended) CoralRed.copy(0.12f) else Color.Green.copy(0.12f), shape = RoundedCornerShape(4.dp) ) { Text( text = if (isSuspended) "SUSPENDED" else "OPERATIONAL", color = if (isSuspended) CoralRed else Color.Green, fontSize = 8.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp) ) } Switch( checked = !isSuspended, onCheckedChange = { viewModel.toggleGateway(gateway) }, colors = SwitchDefaults.colors( checkedThumbColor = Color.Black, checkedTrackColor = BrandGold, uncheckedThumbColor = TextSecondary, uncheckedTrackColor = DarkGreyGlassElevated ), modifier = Modifier.testTag("switch_${gateway.replace(" ", "_")}") ) } } } } } } item { Spacer(modifier = Modifier.height(30.dp)) } } } @Composable fun AdminModelsTab( viewModel: PortalViewModel, models: List ) { var editingModel by remember { mutableStateOf(null) } LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { item { Spacer(modifier = Modifier.height(12.dp)) Text("Talent Profile Auditing", style = MaterialTheme.typography.titleLarge, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Approve profiles, apply verification badges, or suspend profiles from search indexes.", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) Spacer(modifier = Modifier.height(8.dp)) } items(models) { model -> Card( modifier = Modifier .fillMaxWidth() .testTag("admin_model_card_${model.id}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column(modifier = Modifier.padding(16.dp)) { Row( verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier .size(48.dp) .clip(CircleShape) .background(LightGold), contentAlignment = Alignment.Center ) { Text(model.name.first().toString(), fontWeight = FontWeight.ExtraBold, color = BrandGold) } Spacer(modifier = Modifier.width(12.dp)) Column(modifier = Modifier.weight(1f)) { Row { Text(model.name, color = TextPrimary, fontWeight = FontWeight.Bold) Spacer(modifier = Modifier.width(4.dp)) if (model.isVerified) { Icon(Icons.Default.Verified, "v", tint = CyanAccent, modifier = Modifier.size(16.dp)) } } Text("${model.membershipStatus} • Active Location: ${model.location}", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } // Badges states Column(horizontalAlignment = Alignment.End) { if (!model.isApproved) { Surface(shape = RoundedCornerShape(4.dp), color = CoralRed.copy(0.15f), contentColor = CoralRed) { Text("Unapproved", style = MaterialTheme.typography.labelSmall, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp), fontWeight = FontWeight.Bold) } } if (model.isSuspended) { Surface(shape = RoundedCornerShape(4.dp), color = CoralRed.copy(0.15f), contentColor = CoralRed) { Text("Suspended", style = MaterialTheme.typography.labelSmall, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp), fontWeight = FontWeight.Bold) } } } } Spacer(modifier = Modifier.height(10.dp)) // Display complete model profile and phone number data check Column( modifier = Modifier .fillMaxWidth() .background(DarkGreyGlassElevated, RoundedCornerShape(6.dp)) .padding(10.dp), verticalArrangement = Arrangement.spacedBy(4.dp) ) { Text("📞 Phone Number: ${model.phone.ifEmpty { "Not set" }}", style = MaterialTheme.typography.bodySmall, color = BrandGold, fontWeight = FontWeight.Bold) Text("✉️ Email: ${model.email}", style = MaterialTheme.typography.bodySmall, color = TextPrimary) Text("💰 Hourly Rate: $${model.hourlyRate}/Hr", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Text("📝 Bio: ${model.bio}", style = MaterialTheme.typography.bodySmall, color = TextPrimary) } Spacer(modifier = Modifier.height(14.dp)) Row( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth() ) { // Approve/Unapprove toggle val appText = if (model.isApproved) "Revoke Appr." else "Approve Profile" Button( onClick = { viewModel.setModelApproval(model.id, !model.isApproved) }, colors = ButtonDefaults.buttonColors( containerColor = if (model.isApproved) DarkGreyGlassElevated else BrandGold, contentColor = if (model.isApproved) TextPrimary else Color.Black ), shape = RoundedCornerShape(8.dp), modifier = Modifier .weight(1f) .height(32.dp) .testTag("admin_approve_${model.id}"), contentPadding = PaddingValues(0.dp) ) { Text(appText, fontSize = 11.sp, fontWeight = FontWeight.Bold) } // Verify toggle Button( onClick = { viewModel.setModelVerification(model.id, !model.isVerified) }, colors = ButtonDefaults.buttonColors(containerColor = CyanAccent, contentColor = Color.Black), shape = RoundedCornerShape(8.dp), modifier = Modifier .weight(1f) .height(32.dp) .testTag("admin_verify_${model.id}"), contentPadding = PaddingValues(0.dp) ) { Text(if (model.isVerified) "Remove Badge" else "Verify Identity", fontSize = 11.sp, fontWeight = FontWeight.Bold) } // Suspend toggle val suspText = if (model.isSuspended) "Reactivate" else "Suspend Profile" Button( onClick = { viewModel.setModelSuspended(model.id, !model.isSuspended) }, colors = ButtonDefaults.buttonColors(containerColor = CoralRed, contentColor = Color.White), shape = RoundedCornerShape(8.dp), modifier = Modifier .weight(1f) .height(32.dp) .testTag("admin_suspend_${model.id}"), contentPadding = PaddingValues(0.dp) ) { Text(suspText, fontSize = 11.sp, fontWeight = FontWeight.Bold) } } Spacer(modifier = Modifier.height(8.dp)) Button( onClick = { editingModel = model }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(8.dp), modifier = Modifier .fillMaxWidth() .height(34.dp) .testTag("admin_edit_model_btn_${model.id}"), contentPadding = PaddingValues(0.dp) ) { Text("✏️ Edit Model Full Details & Contact", fontWeight = FontWeight.Bold, fontSize = 11.sp) } } } } item { Spacer(modifier = Modifier.height(30.dp)) } } if (editingModel != null) { val model = editingModel!! var editName by remember { mutableStateOf(model.name) } var editEmail by remember { mutableStateOf(model.email) } var editPhone by remember { mutableStateOf(model.phone) } var editRate by remember { mutableStateOf(model.hourlyRate.toString()) } var editLocation by remember { mutableStateOf(model.location) } var editBio by remember { mutableStateOf(model.bio) } var editMembership by remember { mutableStateOf(model.membershipStatus) } var editVerified by remember { mutableStateOf(model.isVerified) } Dialog(onDismissRequest = { editingModel = null }) { Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { LazyColumn( modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { item { Text( text = "Edit Model Profile (Admin)", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = BrandGold ) } item { OutlinedTextField( value = editName, onValueChange = { editName = it }, label = { Text("Name", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editEmail, onValueChange = { editEmail = it }, label = { Text("Email", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editPhone, onValueChange = { editPhone = it }, label = { Text("Phone Number", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editRate, onValueChange = { editRate = it }, label = { Text("Hourly Rate ($ USD)", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editLocation, onValueChange = { editLocation = it }, label = { Text("Location Address", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editBio, onValueChange = { editBio = it }, label = { Text("Bio / Biography", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), maxLines = 3, modifier = Modifier.fillMaxWidth() ) } item { Text("Membership Tier", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { listOf("Free Plan", "VIP Plan", "Premium Plan").forEach { plan -> Box( modifier = Modifier .weight(1f) .clickable { editMembership = plan } .background(if (editMembership == plan) BrandGold.copy(0.15f) else DarkGreyGlass) .border(1.dp, if (editMembership == plan) BrandGold else Color.Transparent, RoundedCornerShape(8.dp)) .padding(vertical = 10.dp), contentAlignment = Alignment.Center ) { Text(plan, fontSize = 11.sp, fontWeight = FontWeight.Bold, color = if (editMembership == plan) BrandGold else TextSecondary) } } } } item { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth() ) { Checkbox( checked = editVerified, onCheckedChange = { editVerified = it }, colors = CheckboxDefaults.colors(checkedColor = CyanAccent, uncheckedColor = TextSecondary) ) Text("Manually Verified Badge", color = TextPrimary) } } item { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth() ) { OutlinedButton( onClick = { editingModel = null }, modifier = Modifier.weight(1f) ) { Text("Cancel") } Button( onClick = { val updatedModel = model.copy( name = editName, email = editEmail, phone = editPhone, hourlyRate = editRate.toDoubleOrNull() ?: model.hourlyRate, location = editLocation, bio = editBio, membershipStatus = editMembership, isVerified = editVerified ) viewModel.adminUpdateModelProfile(updatedModel) editingModel = null }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier.weight(1f) ) { Text("Save Changes", fontWeight = FontWeight.Bold) } } } } } } } } @Composable fun AdminUsersTab( viewModel: PortalViewModel, userWallets: List ) { var showEditWalletDialog by remember { mutableStateOf(null) } var adjustAmountInput by remember { mutableStateOf("") } var adjustTypeIsAdd by remember { mutableStateOf(true) } var editingUser by remember { mutableStateOf(null) } LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { item { Spacer(modifier = Modifier.height(12.dp)) Text("Client Account Auditing", style = MaterialTheme.typography.titleLarge, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Manage user wallets, credit deposits, adjust balances for dispute resolution.", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) Spacer(modifier = Modifier.height(8.dp)) } items(userWallets) { user -> Card( modifier = Modifier .fillMaxWidth() .testTag("admin_user_card_${user.userId}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column(modifier = Modifier.padding(16.dp)) { Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth() ) { Column { Row(verticalAlignment = Alignment.CenterVertically) { val isFrozen = viewModel.frozenUsers.contains(user.userId) Text( text = if (isFrozen) "${user.name} ❄️ [FROZEN]" else user.name, style = MaterialTheme.typography.titleMedium, color = if (isFrozen) CoralRed else TextPrimary, fontWeight = FontWeight.Bold ) if (user.verificationStatus == "verified") { Spacer(modifier = Modifier.width(4.dp)) Icon(Icons.Default.Verified, "Verified Client", tint = Color.Green, modifier = Modifier.size(16.dp)) } } Text(user.email, style = MaterialTheme.typography.bodySmall, color = TextSecondary) Spacer(modifier = Modifier.height(2.dp)) Text("📞 Phone: ${user.phone.ifEmpty { "Not Provided" }}", style = MaterialTheme.typography.bodySmall, color = BrandGold, fontWeight = FontWeight.SemiBold) Spacer(modifier = Modifier.height(4.dp)) Surface(shape = RoundedCornerShape(4.dp), color = BrandGold.copy(0.15f), contentColor = BrandGold) { Text(user.membershipStatus, style = MaterialTheme.typography.labelSmall, modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp), fontWeight = FontWeight.Bold) } } Column(horizontalAlignment = Alignment.End) { Text("Available", style = MaterialTheme.typography.labelSmall, color = TextSecondary) Text(String.format(Locale.US, "$%,.2f", user.availableBalance), style = MaterialTheme.typography.bodyLarge, color = Color.Green, fontWeight = FontWeight.ExtraBold) Text("Escrowed total: $${user.totalBalance}", style = MaterialTheme.typography.labelSmall, color = TextSecondary) } } Spacer(modifier = Modifier.height(12.dp)) HorizontalDivider(color = TextSecondary.copy(0.1f)) Spacer(modifier = Modifier.height(12.dp)) // 🛡️ USER VERIFICATION STATUS SECTION Text("Identity & Trust Verification", style = MaterialTheme.typography.labelSmall, color = BrandGold, fontWeight = FontWeight.Bold) Spacer(modifier = Modifier.height(6.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f)) { val statusIcon = if (user.verificationStatus == "verified") Icons.Default.VerifiedUser else Icons.Default.Person val statusColor = when (user.verificationStatus) { "verified" -> Color.Green "pending" -> BrandGold "rejected" -> CoralRed else -> TextSecondary } Icon( imageVector = statusIcon, contentDescription = "status icon", tint = statusColor, modifier = Modifier.size(20.dp) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = "Status: ${user.verificationStatus.uppercase()}", style = MaterialTheme.typography.bodySmall, color = statusColor, fontWeight = FontWeight.Bold ) if (user.nidNumber.isNotEmpty()) { Text( text = "NID: ${user.nidNumber}", style = MaterialTheme.typography.labelSmall, color = TextPrimary ) } else { Text( text = "No KYC documents submitted yet", style = MaterialTheme.typography.labelSmall, color = TextSecondary ) } } } // Verify & Reject Actions Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) { if (user.verificationStatus != "verified") { Button( onClick = { viewModel.setUserKYCStatus(user.userId, "verified") }, colors = ButtonDefaults.buttonColors(containerColor = Color.Green, contentColor = Color.Black), shape = RoundedCornerShape(6.dp), modifier = Modifier.height(30.dp).testTag("admin_verify_user_btn_${user.userId}"), contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp) ) { Text("Verify", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } if (user.verificationStatus != "rejected" && user.verificationStatus != "unverified") { Button( onClick = { viewModel.setUserKYCStatus(user.userId, "rejected") }, colors = ButtonDefaults.buttonColors(containerColor = CoralRed, contentColor = Color.White), shape = RoundedCornerShape(6.dp), modifier = Modifier.height(30.dp).testTag("admin_reject_user_btn_${user.userId}"), contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp) ) { Text("Reject", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } if (user.verificationStatus == "verified") { OutlinedButton( onClick = { viewModel.setUserKYCStatus(user.userId, "unverified") }, shape = RoundedCornerShape(6.dp), modifier = Modifier.height(30.dp).testTag("admin_unverify_user_btn_${user.userId}"), border = BorderStroke(1.dp, TextSecondary.copy(0.5f)), colors = ButtonDefaults.outlinedButtonColors(contentColor = TextSecondary), contentPadding = PaddingValues(horizontal = 10.dp, vertical = 0.dp) ) { Text("Revoke", fontSize = 11.sp, fontWeight = FontWeight.SemiBold) } } } } Spacer(modifier = Modifier.height(14.dp)) val isFrozen = viewModel.frozenUsers.contains(user.userId) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Button( onClick = { showEditWalletDialog = user }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(8.dp), modifier = Modifier .weight(1.1f) .height(34.dp) .testTag("admin_wallet_override_btn"), contentPadding = PaddingValues(0.dp) ) { Text("💸 Adjust USD", fontWeight = FontWeight.Bold, fontSize = 11.sp) } Button( onClick = { editingUser = user }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(8.dp), modifier = Modifier .weight(1f) .height(34.dp) .testTag("admin_edit_user_btn_${user.userId}"), contentPadding = PaddingValues(0.dp) ) { Text("✏️ Edit Data", fontWeight = FontWeight.Bold, fontSize = 11.sp) } Button( onClick = { viewModel.toggleUserFreeze(user.userId) }, colors = ButtonDefaults.buttonColors( containerColor = if (isFrozen) Color.Green else CoralRed, contentColor = if (isFrozen) Color.Black else Color.White ), shape = RoundedCornerShape(8.dp), modifier = Modifier .weight(1.1f) .height(34.dp) .testTag("admin_freeze_user_btn_${user.userId}"), contentPadding = PaddingValues(0.dp) ) { Text(if (isFrozen) "🔥 Unfreeze" else "❄️ Freeze", fontWeight = FontWeight.Bold, fontSize = 11.sp) } } } } } item { Spacer(modifier = Modifier.height(30.dp)) } } if (editingUser != null) { val user = editingUser!! var editName by remember { mutableStateOf(user.name) } var editEmail by remember { mutableStateOf(user.email) } var editPhone by remember { mutableStateOf(user.phone) } var editNid by remember { mutableStateOf(user.nidNumber) } var editAvailable by remember { mutableStateOf(user.availableBalance.toString()) } var editTotal by remember { mutableStateOf(user.totalBalance.toString()) } var editMembership by remember { mutableStateOf(user.membershipStatus) } var editIsAgent by remember { mutableStateOf(user.isAgent) } Dialog(onDismissRequest = { editingUser = null }) { Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { LazyColumn( modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { item { Text( text = "Edit Client Profile (Admin)", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = BrandGold ) } item { OutlinedTextField( value = editName, onValueChange = { editName = it }, label = { Text("Name", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editEmail, onValueChange = { editEmail = it }, label = { Text("Email", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editPhone, onValueChange = { editPhone = it }, label = { Text("Phone Number", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editNid, onValueChange = { editNid = it }, label = { Text("National ID (NID)", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editAvailable, onValueChange = { editAvailable = it }, label = { Text("Available Wallet Balance ($)", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = editTotal, onValueChange = { editTotal = it }, label = { Text("Escrowed Balance / Total ($)", color = TextSecondary) }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), modifier = Modifier.fillMaxWidth() ) } item { Text("Membership Tier", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { listOf("Free Membership", "VIP Membership", "Premium Membership").forEach { plan -> Box( modifier = Modifier .weight(1f) .clickable { editMembership = plan } .background(if (editMembership == plan) BrandGold.copy(0.15f) else DarkGreyGlass) .border(1.dp, if (editMembership == plan) BrandGold else Color.Transparent, RoundedCornerShape(8.dp)) .padding(vertical = 10.dp), contentAlignment = Alignment.Center ) { Text(plan, fontSize = 9.sp, fontWeight = FontWeight.Bold, color = if (editMembership == plan) BrandGold else TextSecondary) } } } } item { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth() ) { Checkbox( checked = editIsAgent, onCheckedChange = { editIsAgent = it }, colors = CheckboxDefaults.colors(checkedColor = Color.Green, uncheckedColor = TextSecondary) ) Text("Authorized Cash Agent Status", color = TextPrimary) } } item { Row( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth() ) { OutlinedButton( onClick = { editingUser = null }, modifier = Modifier.weight(1f) ) { Text("Cancel") } Button( onClick = { val updatedUser = user.copy( name = editName, email = editEmail, phone = editPhone, nidNumber = editNid, availableBalance = editAvailable.toDoubleOrNull() ?: user.availableBalance, totalBalance = editTotal.toDoubleOrNull() ?: user.totalBalance, membershipStatus = editMembership, isAgent = editIsAgent ) viewModel.adminUpdateUserProfile(updatedUser) editingUser = null }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier.weight(1f) ) { Text("Save Changes", fontWeight = FontWeight.Bold) } } } } } } } showEditWalletDialog?.let { user -> Dialog(onDismissRequest = { showEditWalletDialog = null }) { Card( colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), shape = RoundedCornerShape(20.dp), modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Column(modifier = Modifier.padding(24.dp)) { Text("Ledger Balance Override", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Credit bounds for user ${user.name}", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Spacer(modifier = Modifier.height(16.dp)) Row( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth() ) { Surface( modifier = Modifier .weight(1f) .clickable { adjustTypeIsAdd = true } .border(1.dp, if (adjustTypeIsAdd) BrandGold else Color.Transparent, RoundedCornerShape(8.dp)), shape = RoundedCornerShape(8.dp), color = if (adjustTypeIsAdd) BrandGold.copy(0.15f) else DarkGreyGlass ) { Text("Credit (Add)", modifier = Modifier.padding(vertical = 10.dp), textAlign = TextAlign.Center, color = if (adjustTypeIsAdd) BrandGold else TextSecondary, fontWeight = FontWeight.Bold) } Surface( modifier = Modifier .weight(1f) .clickable { adjustTypeIsAdd = false } .border(1.dp, if (!adjustTypeIsAdd) BrandGold else Color.Transparent, RoundedCornerShape(8.dp)), shape = RoundedCornerShape(8.dp), color = if (!adjustTypeIsAdd) BrandGold.copy(0.15f) else DarkGreyGlass ) { Text("Debit (Subtract)", modifier = Modifier.padding(vertical = 10.dp), textAlign = TextAlign.Center, color = if (!adjustTypeIsAdd) BrandGold else TextSecondary, fontWeight = FontWeight.Bold) } } Spacer(modifier = Modifier.height(16.dp)) TextField( value = adjustAmountInput, onValueChange = { adjustAmountInput = it }, placeholder = { Text("Amount ($ USD)") }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true, modifier = Modifier .fillMaxWidth() .testTag("admin_override_input") ) Spacer(modifier = Modifier.height(24.dp)) Row( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth() ) { OutlinedButton( onClick = { showEditWalletDialog = null }, modifier = Modifier.weight(1f) ) { Text("Cancel") } Button( onClick = { val amtNum = adjustAmountInput.toDoubleOrNull() ?: 0.0 if (amtNum > 0.0) { viewModel.manageUserWalletAdmin(user.userId, amtNum, adjustTypeIsAdd) } adjustAmountInput = "" showEditWalletDialog = null }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier .weight(1f) .testTag("admin_override_submit") ) { Text("Save", fontWeight = FontWeight.Bold) } } } } } } } @OptIn(ExperimentalMaterial3Api::class) @Composable fun AdminChatMonitorDialog( booking: BookingEntity, viewModel: PortalViewModel, onDismiss: () -> Unit ) { // Fetch private chats flow val chats by viewModel.getPrivateChats(booking.id).collectAsState(initial = emptyList()) var textInput by remember { mutableStateOf("") } val listState = rememberLazyListState() // Auto-scroll on new messages LaunchedEffect(chats.size) { if (chats.isNotEmpty()) { listState.animateScrollToItem(chats.size - 1) } } Dialog( onDismissRequest = onDismiss ) { Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Obsidian), modifier = Modifier .fillMaxWidth(0.95f) .fillMaxHeight(0.85f), border = BorderStroke(1.dp, BrandGold.copy(0.3f)) ) { Column(modifier = Modifier.fillMaxSize()) { // Header Bar Surface( color = DarkGreyGlassElevated, modifier = Modifier.fillMaxWidth() ) { Row( modifier = Modifier .fillMaxWidth() .padding(14.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Column { Text( text = "Secure Escrow Intermediary Console", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold ) Text( text = "Contract #${booking.id} • ${booking.userName} ⇆ ${booking.modelName}", style = MaterialTheme.typography.labelSmall, color = TextSecondary ) } IconButton(onClick = onDismiss) { Icon( imageVector = Icons.Default.Close, contentDescription = "Close", tint = TextSecondary ) } } } // Privacy Warning Surface( color = LightGold, modifier = Modifier.fillMaxWidth() ) { Row( modifier = Modifier.padding(horizontal = 14.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = Icons.Default.Lock, contentDescription = null, tint = BrandGold, modifier = Modifier.size(12.dp) ) Spacer(modifier = Modifier.width(6.dp)) Text( text = "Admin Mode: You are monitoring this active contract room. Messages sent will appear as [System Intervention].", fontSize = 11.sp, color = BrandGold, fontWeight = FontWeight.Medium ) } } // Chat Messages List Box( modifier = Modifier .weight(1f) .fillMaxWidth() .background(Color.White) .padding(horizontal = 14.dp) ) { if (chats.isEmpty()) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text( text = "No messages have been exchanged in this contract room.", style = MaterialTheme.typography.bodySmall, color = TextSecondary ) } } else { LazyColumn( state = listState, modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(vertical = 12.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { items(chats) { chat -> val alignment = when (chat.senderType) { "USER" -> Alignment.Start "MODEL" -> Alignment.End else -> Alignment.CenterHorizontally } val bubbleBg = when (chat.senderType) { "USER" -> DarkGreyGlassElevated "MODEL" -> BrandGold else -> LightGold } val textColor = when (chat.senderType) { "USER" -> TextPrimary "MODEL" -> Color.White else -> BrandGold } val bubbleShape = RoundedCornerShape(12.dp) Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = alignment ) { Text( text = "${chat.senderName} (${chat.senderType})", style = MaterialTheme.typography.labelSmall, color = TextSecondary, modifier = Modifier.padding(horizontal = 4.dp, vertical = 2.dp) ) Surface( color = bubbleBg, shape = bubbleShape, border = BorderStroke(1.dp, Color.LightGray.copy(0.2f)) ) { Column(modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp)) { Text( text = chat.message, color = textColor, style = MaterialTheme.typography.bodyMedium ) } } } } } } } // Chat Input Panel Surface( color = DarkGreyGlassElevated, modifier = Modifier.fillMaxWidth() ) { Row( modifier = Modifier .fillMaxWidth() .padding(10.dp), verticalAlignment = Alignment.CenterVertically ) { TextField( value = textInput, onValueChange = { textInput = it }, placeholder = { Text("Intervene or send system notice...", fontSize = 14.sp) }, modifier = Modifier.weight(1f), colors = TextFieldDefaults.colors( focusedContainerColor = Color.White, unfocusedContainerColor = Color.White, focusedIndicatorColor = Color.Transparent, unfocusedIndicatorColor = Color.Transparent ), shape = RoundedCornerShape(20.dp), singleLine = true ) Spacer(modifier = Modifier.width(8.dp)) IconButton( onClick = { if (textInput.isNotBlank()) { viewModel.sendPrivateMessage( bookingId = booking.id, senderId = "SYSTEM", senderName = "System Admin", senderType = "SYSTEM", message = textInput ) textInput = "" } }, modifier = Modifier .clip(CircleShape) .background(BrandGold) .size(40.dp) ) { Icon( imageVector = Icons.Default.Send, contentDescription = "Send Intervening Message", tint = Color.White, modifier = Modifier.size(18.dp) ) } } } } } } } @Composable fun AdminReportsTab( viewModel: PortalViewModel, logs: List, bookings: List, userWallets: List, models: List ) { val completedB = bookings.filter { it.status == "COMPLETED" } val commissionSum = completedB.sumOf { it.totalPrice * 0.15 } val activeB = bookings.filter { it.status == "ACCEPTED" || it.status == "PENDING" } val activeEscrowVal = activeB.sumOf { it.totalPrice } var monitoringBooking by remember { mutableStateOf(null) } LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { item { Spacer(modifier = Modifier.height(12.dp)) Text("Ledger & Escrow Communications", style = MaterialTheme.typography.titleLarge, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Live monitoring of client-to-model secure bookings, contact verification, and escrow audit.", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) Spacer(modifier = Modifier.height(12.dp)) } // Summary Statistics Card item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column(modifier = Modifier.padding(16.dp)) { Text("Reconciliation Breakdown", color = BrandGold, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold) Spacer(modifier = Modifier.height(10.dp)) Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Text("Aggregated Payouts (85% models):", color = TextSecondary, style = MaterialTheme.typography.bodySmall) Text(String.format(Locale.US, "$%,.2f", completedB.sumOf { it.totalPrice * 0.85 }), color = TextPrimary, fontWeight = FontWeight.Bold) } Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Text("Total Platform Commission (15%):", color = TextSecondary, style = MaterialTheme.typography.bodySmall) Text(String.format(Locale.US, "$%,.2f", commissionSum), color = CyanAccent, fontWeight = FontWeight.Bold) } Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Text("Active Escrow Escaper Bounds:", color = TextSecondary, style = MaterialTheme.typography.bodySmall) Text(String.format(Locale.US, "$%,.2f", activeEscrowVal), color = BrandGold, fontWeight = FontWeight.Bold) } } } } // Title for Bookings Monitoring section item { Text("Active Secure Booking Rooms & Phone Checks", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) } if (bookings.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( "No booking contracts or communication channels registered in escrow yet.", modifier = Modifier .fillMaxWidth() .padding(24.dp), textAlign = TextAlign.Center, color = TextSecondary, style = MaterialTheme.typography.bodySmall ) } } } else { items(bookings) { booking -> val clientWallet = userWallets.find { it.userId == booking.userId } val modelProfile = models.find { it.id == booking.modelId } Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), border = BorderStroke(1.dp, BrandGold.copy(0.15f)) ) { Column(modifier = Modifier.padding(14.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text( text = "Booking Room #${booking.id}", style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold, color = BrandGold ) Surface( shape = RoundedCornerShape(4.dp), color = when (booking.status) { "COMPLETED" -> Color.Green.copy(0.15f) "ACCEPTED" -> CyanAccent.copy(0.15f) "CANCELLED" -> CoralRed.copy(0.15f) else -> BrandGold.copy(0.15f) }, contentColor = when (booking.status) { "COMPLETED" -> Color.Green "ACCEPTED" -> CyanAccent "CANCELLED" -> CoralRed else -> BrandGold } ) { Text( text = booking.status.uppercase(), style = MaterialTheme.typography.labelSmall, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp) ) } } Spacer(modifier = Modifier.height(8.dp)) // Show client phone number and details Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Column(modifier = Modifier.weight(1f)) { Text("👤 Buyer/Client Details:", style = MaterialTheme.typography.bodySmall, color = TextSecondary, fontWeight = FontWeight.Bold) Text("${booking.userName} (${booking.userId})", style = MaterialTheme.typography.bodySmall, color = TextPrimary) Text("📞 Client Phone: ${clientWallet?.phone?.ifEmpty { "Not Provided" } ?: "Not Set"}", style = MaterialTheme.typography.bodySmall, color = BrandGold) Text("✉️ Client Email: ${clientWallet?.email ?: "Unknown"}", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } Spacer(modifier = Modifier.width(16.dp)) // Show model phone number and details Column(modifier = Modifier.weight(1f)) { Text("💃 Seller/Model Details:", style = MaterialTheme.typography.bodySmall, color = TextSecondary, fontWeight = FontWeight.Bold) Text("${booking.modelName} (Model #${booking.modelId})", style = MaterialTheme.typography.bodySmall, color = TextPrimary) Text("📞 Model Phone: ${modelProfile?.phone?.ifEmpty { "Not Provided" } ?: "Not Set"}", style = MaterialTheme.typography.bodySmall, color = BrandGold) Text("✉️ Model Email: ${modelProfile?.email ?: "Unknown"}", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } } Spacer(modifier = Modifier.height(10.dp)) HorizontalDivider(color = TextSecondary.copy(0.1f)) Spacer(modifier = Modifier.height(10.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text( text = "Contract Value: $${booking.totalPrice}", style = MaterialTheme.typography.bodySmall, fontWeight = FontWeight.Bold, color = TextPrimary ) Button( onClick = { monitoringBooking = booking }, shape = RoundedCornerShape(6.dp), colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier.height(30.dp), contentPadding = PaddingValues(horizontal = 14.dp, vertical = 0.dp) ) { Text("💬 Audit Secure 1vs1 Chat", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } } } } item { Text("System-Wide Ledger Log", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) } if (logs.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( "No events logged yet.", modifier = Modifier .fillMaxWidth() .padding(24.dp), textAlign = TextAlign.Center, color = TextSecondary ) } } } else { items(logs) { log -> Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Row( modifier = Modifier.padding(14.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Column { Text(log.description, style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.SemiBold) Text("Account: ${log.accountId} • Class: ${log.type}", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } val pre = if (log.amount > 0) "+" else "" Text( String.format(Locale.US, "%s$%,.2f", pre, log.amount), color = if (log.amount > 0) Color.Green else TextPrimary, fontWeight = FontWeight.Bold ) } } } } item { Spacer(modifier = Modifier.height(30.dp)) } } if (monitoringBooking != null) { AdminChatMonitorDialog( booking = monitoringBooking!!, viewModel = viewModel, onDismiss = { monitoringBooking = null } ) } } @Composable fun StatusPill(label: String, verified: Boolean) { Surface( shape = RoundedCornerShape(4.dp), color = if (verified) Color.Green.copy(0.1f) else Color.White.copy(0.05f), contentColor = if (verified) Color.Green else TextSecondary ) { Text( text = "$label: ${if (verified) "YES" else "NO"}", style = MaterialTheme.typography.labelSmall, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp), fontSize = 9.sp ) } } @Composable fun KYCStatusPill(label: String, status: String) { val (text, color, bg) = when (status) { "verified" -> Triple("APPROVED", Color.Green, Color.Green.copy(0.1f)) "pending" -> Triple("PENDING", BrandGold, BrandGold.copy(0.11f)) "rejected" -> Triple("REJECTED", CoralRed, CoralRed.copy(0.1f)) else -> Triple("UNVERIFIED", TextSecondary, Color.White.copy(0.05f)) } Surface( shape = RoundedCornerShape(4.dp), color = bg, contentColor = color ) { Text( text = "$label: $text", style = MaterialTheme.typography.labelSmall, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp), fontSize = 9.sp ) } } @Composable fun AdminAgentsTab( viewModel: PortalViewModel, userWallets: List ) { var fundingAmountInput by remember { mutableStateOf("") } var selectedWalletToFund by remember { mutableStateOf(null) } // Agent Creation form states var showCreateAgentDialog by remember { mutableStateOf(false) } var createName by remember { mutableStateOf("") } var createPhone by remember { mutableStateOf("") } var createEmail by remember { mutableStateOf("") } var createCountry by remember { mutableStateOf("Bangladesh") } var createCity by remember { mutableStateOf("Dhaka") } var createNid by remember { mutableStateOf("") } var createInitialBalance by remember { mutableStateOf("10000.0") } var createPass by remember { mutableStateOf("password123") } // Filter wallets dynamically val pendingApplications = userWallets.filter { it.agentApplicationStatus == "pending" } val approvedAgents = userWallets.filter { it.isAgent } val depositRequests = viewModel.cashDepositRequests.collectAsState(initial = emptyList()).value val withdrawals = viewModel.allWithdrawalsFlow.collectAsState(initial = emptyList()).value LazyColumn( modifier = Modifier .fillMaxSize() .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { // Tab Heading item { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column(modifier = Modifier.weight(1f)) { Text( text = "⚡ ADAPTIVE AGENT GATEWAY", style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.ExtraBold, color = BrandGold, modifier = Modifier.testTag("admin_agents_title") ) Text( text = "Provision virtual nodes, inject capital, toggle active locks, and verify agents.", style = MaterialTheme.typography.bodySmall, color = TextSecondary ) } Button( onClick = { showCreateAgentDialog = true }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(8.dp), modifier = Modifier.testTag("admin_provision_agent_btn") ) { Icon(Icons.Default.Add, "add_agent", modifier = Modifier.size(16.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Provision Node", fontWeight = FontWeight.SemiBold, fontSize = 12.sp) } } } // Section 1: Pending Applications item { Text( text = "Pending Cash Agent Licenses (${pendingApplications.size})", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = TextPrimary ) } if (pendingApplications.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( text = "No pending agent applications in authorization queue.", style = MaterialTheme.typography.bodySmall, color = TextSecondary, modifier = Modifier.padding(16.dp), textAlign = TextAlign.Center ) } } } else { items(pendingApplications) { wallet -> Card( modifier = Modifier .fillMaxWidth() .testTag("agent_appl_${wallet.userId}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), border = BorderStroke(1.dp, BrandGold.copy(0.4f)) ) { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text(wallet.name, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, color = TextPrimary) Surface(color = BrandGold.copy(0.12f), shape = RoundedCornerShape(4.dp)) { Text("PENDING LICENSE", color = BrandGold, fontSize = 9.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)) } } Text("User ID: ${wallet.userId}", style = MaterialTheme.typography.labelSmall, color = TextSecondary) Text("Phone Number: ${wallet.agentPhone}", style = MaterialTheme.typography.bodyMedium, color = TextPrimary) Text("National ID (NID): ${wallet.agentNid}", style = MaterialTheme.typography.bodyMedium, color = TextPrimary) Text("Outlet Address: ${wallet.agentLocation}", style = MaterialTheme.typography.bodyMedium, color = TextPrimary) Spacer(modifier = Modifier.height(4.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Button( onClick = { viewModel.approveCashAgent(wallet.userId) }, colors = ButtonDefaults.buttonColors(containerColor = Color.Green, contentColor = Color.White), modifier = Modifier.weight(1f).testTag("approve_agent_btn_${wallet.userId}"), shape = RoundedCornerShape(8.dp) ) { Text("Approve License", fontWeight = FontWeight.Bold, fontSize = 12.sp) } OutlinedButton( onClick = { viewModel.rejectCashAgent(wallet.userId) }, colors = ButtonDefaults.outlinedButtonColors(contentColor = CoralRed), border = BorderStroke(1.dp, CoralRed), modifier = Modifier.weight(1f).testTag("reject_agent_btn_${wallet.userId}"), shape = RoundedCornerShape(8.dp) ) { Text("Reject", fontWeight = FontWeight.Bold, fontSize = 12.sp) } } } } } } // Section 2: Active Agents list item { Text( text = "Licensed Cash Agents (${approvedAgents.size})", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = TextPrimary ) } if (approvedAgents.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( text = "No approved cash agents active in system. Authorize users from the queue or provision one above.", style = MaterialTheme.typography.bodySmall, color = TextSecondary, modifier = Modifier.padding(16.dp), textAlign = TextAlign.Center ) } } } else { items(approvedAgents) { agent -> val isFrozen = viewModel.frozenUsers.contains(agent.userId) Card( modifier = Modifier .fillMaxWidth() .testTag("active_agent_${agent.userId}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), border = if (isFrozen) BorderStroke(1.dp, CoralRed.copy(0.4f)) else null ) { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text(agent.name, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, color = TextPrimary) Surface( color = if (isFrozen) CoralRed.copy(0.15f) else Color.Green.copy(0.15f), shape = RoundedCornerShape(4.dp) ) { Text( text = if (isFrozen) "FROZEN NODE" else "ACTIVE AGENT", color = if (isFrozen) CoralRed else Color.Green, fontSize = 9.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp) ) } } Text("Phone: ${agent.agentPhone} • Location: ${agent.agentLocation}", style = MaterialTheme.typography.bodySmall, color = TextSecondary) // Financial Grid Panel Row( modifier = Modifier .fillMaxWidth() .background(DarkGreyGlass, RoundedCornerShape(8.dp)) .padding(12.dp), horizontalArrangement = Arrangement.SpaceBetween ) { Column { Text("Escrow Funded Balance", style = MaterialTheme.typography.labelSmall, color = TextSecondary) Text(String.format(Locale.US, "৳%,.2f BDT", agent.agentBalance), color = BrandGold, fontWeight = FontWeight.Black, style = MaterialTheme.typography.titleMedium) } Column { Text("Commission Earned", style = MaterialTheme.typography.labelSmall, color = TextSecondary) Text(String.format(Locale.US, "৳%,.2f BDT", agent.agentCommissionEarned), color = Color.Green, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.titleMedium) } } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text( text = String.format(Locale.US, "Total Handled: ৳%,.0f BDT", agent.agentTotalCashCollected), style = MaterialTheme.typography.labelSmall, color = TextSecondary ) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { Button( onClick = { viewModel.toggleUserFreeze(agent.userId) }, colors = ButtonDefaults.buttonColors( containerColor = if (isFrozen) Color.Green else CoralRed, contentColor = if (isFrozen) Color.Black else Color.White ), shape = RoundedCornerShape(6.dp), modifier = Modifier.height(32.dp), contentPadding = PaddingValues(horizontal = 10.dp) ) { Text(if (isFrozen) "Unfreeze" else "Freeze Node", fontWeight = FontWeight.Bold, fontSize = 10.sp) } Button( onClick = { selectedWalletToFund = agent }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier.testTag("fund_agent_btn_${agent.userId}"), shape = RoundedCornerShape(6.dp), contentPadding = PaddingValues(horizontal = 12.dp, vertical = 6.dp) ) { Icon(Icons.Default.Add, "add", modifier = Modifier.size(14.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Fund Escrow", fontWeight = FontWeight.Bold, fontSize = 11.sp) } } } } } } } // Section 3: Pending Deposit Verification & Approvals (Admin approves to credit User wallet) item { Spacer(modifier = Modifier.height(8.dp)) Text( text = "Deposit Clearances Approval Queue (${depositRequests.filter { it.status == "CONFIRMED" }.size})", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = TextPrimary ) } val confirmableDeposits = depositRequests.filter { it.status == "CONFIRMED" } if (confirmableDeposits.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( text = "No agent-confirmed deposit clearings awaiting admin release.", style = MaterialTheme.typography.bodySmall, color = TextSecondary, modifier = Modifier.padding(16.dp), textAlign = TextAlign.Center ) } } } else { items(confirmableDeposits) { req -> Card( modifier = Modifier .fillMaxWidth() .testTag("admin_confirm_dep_${req.id}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), border = BorderStroke(1.dp, Color.Cyan.copy(0.3f)) ) { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text("Bill Clearing #${req.id}", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, color = BrandGold) Surface(color = Color.Cyan.copy(0.12f), shape = RoundedCornerShape(4.dp)) { Text("AGENT CONFIRMED", color = Color.Cyan, fontSize = 9.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)) } } Text("Client Name: ${req.userName} (Id: ${req.userId})", style = MaterialTheme.typography.bodyMedium, color = TextPrimary) Text("Amount: ৳${req.amount} BDT • Net Fee: ৳${req.commission} BDT", style = MaterialTheme.typography.bodyMedium, color = TextPrimary) Text("Wallet: ${req.walletNumber} • Trx ID Last 4: ${req.trxLast4}", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) Text("Node Agent: ${req.agentName} (Id: ${req.agentId})", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) if (req.screenshot.isNotEmpty()) { Row( modifier = Modifier .fillMaxWidth() .background(Color.White.copy(0.04f), RoundedCornerShape(4.dp)) .padding(8.dp), verticalAlignment = Alignment.CenterVertically ) { Icon(Icons.Default.Image, "img", tint = Color.Green, modifier = Modifier.size(14.dp)) Spacer(modifier = Modifier.width(6.dp)) Text("Receipt proof file: ${req.screenshot}", fontSize = 11.sp, color = TextSecondary) } } Button( onClick = { viewModel.approveCashDeposit(req.id) { _, _ -> } }, colors = ButtonDefaults.buttonColors(containerColor = Color.Green, contentColor = Color.White), modifier = Modifier.fillMaxWidth().testTag("admin_approve_deposit_btn_${req.id}"), shape = RoundedCornerShape(8.dp) ) { Text("Credit User Wallet & Release Ledger", fontWeight = FontWeight.Bold, fontSize = 12.sp) } } } } } // Section 4: Model Payout Settlement Queue (Admin approves payout and clears agent reserve limit) item { Spacer(modifier = Modifier.height(8.dp)) Text( text = "Model Payout Release Approvals (${withdrawals.filter { it.status == "SENT" }.size})", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = TextPrimary ) } val approveablePayouts = withdrawals.filter { it.status == "SENT" } if (approveablePayouts.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( text = "No agent-pushed payouts awaiting admin release.", style = MaterialTheme.typography.bodySmall, color = TextSecondary, modifier = Modifier.padding(16.dp), textAlign = TextAlign.Center ) } } } else { items(approveablePayouts) { wt -> Card( modifier = Modifier .fillMaxWidth() .testTag("admin_payout_wt_${wt.id}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), border = BorderStroke(1.dp, Color.Magenta.copy(0.3f)) ) { Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text("Payout Order #${wt.id}", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, color = BrandGold) Surface(color = Color.Cyan.copy(0.12f), shape = RoundedCornerShape(4.dp)) { Text("SENT BY AGENT", color = Color.Cyan, fontSize = 9.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)) } } Text("Payee Model Name: ${wt.modelName} (Id: ${wt.modelId})", style = MaterialTheme.typography.bodyMedium, color = TextPrimary) Text("Acc Name: ${wt.name}", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) Text("Wallet Send Money Target Number: ${wt.walletNumber}", style = MaterialTheme.typography.bodyMedium, color = TextPrimary) Text("Amount: ৳${wt.amount} BDT", style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold, color = BrandGold) Text("Agent Handler Node: ${wt.agentName} (Id: ${wt.agentId})", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) if (wt.screenshot.isNotEmpty()) { Row( modifier = Modifier .fillMaxWidth() .background(Color.White.copy(0.04f), RoundedCornerShape(4.dp)) .padding(8.dp), verticalAlignment = Alignment.CenterVertically ) { Icon(Icons.Default.Cloud, "cloud", tint = Color.Green, modifier = Modifier.size(14.dp)) Spacer(modifier = Modifier.width(6.dp)) Text("Agent Electronic payout proof receipt: ${wt.screenshot}", fontSize = 11.sp, color = TextSecondary) } } Button( onClick = { viewModel.adminConfirmWithdrawal(wt.id) { _, _ -> } }, colors = ButtonDefaults.buttonColors(containerColor = Color.Green, contentColor = Color.White), modifier = Modifier.fillMaxWidth().testTag("admin_approve_payout_btn_${wt.id}"), shape = RoundedCornerShape(8.dp) ) { Text("Approve Clearance & Finalize Ledger", fontWeight = FontWeight.Bold, fontSize = 12.sp) } } } } } // Section 5: Requested Model Payouts Routing (Admin assigns pending payout to a Cash Agent) item { Spacer(modifier = Modifier.height(16.dp)) Text( text = "Model Payout Dispatch to Cash Agents (${withdrawals.filter { it.status == "PENDING" && it.agentId.isEmpty() }.size})", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = TextPrimary ) } val pendingRoutingPayouts = withdrawals.filter { it.status == "PENDING" && it.agentId.isEmpty() } if (pendingRoutingPayouts.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( text = "No pending model payouts awaiting Cash Agent routing.", style = MaterialTheme.typography.bodySmall, color = TextSecondary, modifier = Modifier.padding(16.dp), textAlign = TextAlign.Center ) } } } else { items(pendingRoutingPayouts) { wt -> Card( modifier = Modifier .fillMaxWidth() .testTag("admin_route_wt_${wt.id}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), border = BorderStroke(1.dp, Color.Yellow.copy(0.3f)) ) { Column(modifier = Modifier.padding(14.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text("Awaiting Route #${wt.id}", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, color = BrandGold) Surface(color = Color.Yellow.copy(0.12f), shape = RoundedCornerShape(4.dp)) { Text("NEEDS ROUTING", color = BrandGold, fontSize = 9.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)) } } Text("Model: ${wt.modelName} (Id: ${wt.modelId})", style = MaterialTheme.typography.bodyMedium, color = TextPrimary) Text("Wallet Number: ${wt.walletNumber} (${wt.name})", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) Text("Requested Amount: ৳${wt.amount} BDT", style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold, color = BrandGold) Text("Select Authorized Agent to Dispatch Payout:", style = MaterialTheme.typography.labelSmall, color = TextPrimary) if (approvedAgents.isEmpty()) { Text("No cash agents online or authorized in directory.", color = CoralRed, style = MaterialTheme.typography.bodySmall) } else { Row( horizontalArrangement = Arrangement.spacedBy(6.dp), modifier = Modifier .fillMaxWidth() .horizontalScroll(rememberScrollState()) ) { approvedAgents.forEach { agent -> Button( onClick = { viewModel.routeWithdrawalToAgent(wt.id, agent.userId) { _, _ -> } }, colors = ButtonDefaults.buttonColors(containerColor = DarkGreyGlass, contentColor = BrandGold), border = BorderStroke(1.dp, BrandGold.copy(0.4f)), shape = RoundedCornerShape(20.dp), contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp), modifier = Modifier.testTag("admin_route_wt_${wt.id}_to_${agent.userId}") ) { Text("${agent.name} (৳${agent.agentBalance})", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } } } } } } item { Spacer(modifier = Modifier.height(40.dp)) } } // Provision Node Dialog if (showCreateAgentDialog) { Dialog(onDismissRequest = { showCreateAgentDialog = false }) { Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), modifier = Modifier .fillMaxWidth() .padding(8.dp) ) { LazyColumn( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(12.dp) ) { item { Text( text = "Provision Sovereign Node", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = BrandGold ) } item { OutlinedTextField( value = createName, onValueChange = { createName = it }, label = { Text("Full Node Location/Name") }, singleLine = true, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth().testTag("prov_agent_name") ) } item { OutlinedTextField( value = createPhone, onValueChange = { createPhone = it }, label = { Text("Contact Phone") }, singleLine = true, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth().testTag("prov_agent_phone") ) } item { OutlinedTextField( value = createEmail, onValueChange = { createEmail = it }, label = { Text("Gateway Credentials Email") }, singleLine = true, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth().testTag("prov_agent_email") ) } item { Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { OutlinedTextField( value = createCountry, onValueChange = { createCountry = it }, label = { Text("Country") }, singleLine = true, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.weight(1f) ) OutlinedTextField( value = createCity, onValueChange = { createCity = it }, label = { Text("City") }, singleLine = true, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.weight(1f) ) } } item { OutlinedTextField( value = createNid, onValueChange = { createNid = it }, label = { Text("Assigned National ID (NID)") }, singleLine = true, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = createInitialBalance, onValueChange = { createInitialBalance = it }, label = { Text("Initial Escrow Capital (৳ BDT)") }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = createPass, onValueChange = { createPass = it }, label = { Text("Secure Workspace Password Hash") }, singleLine = true, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth() ) } item { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { OutlinedButton( onClick = { showCreateAgentDialog = false }, modifier = Modifier.weight(1f) ) { Text("Discard") } Button( onClick = { if (createName.isNotEmpty() && createEmail.isNotEmpty()) { val initialBalanceDouble = createInitialBalance.toDoubleOrNull() ?: 10000.0 viewModel.adminCreateAgentWallet( userId = createEmail, name = createName, email = createEmail, phone = createPhone.ifEmpty { "0172345678" }, isAgent = true, agentBalance = initialBalanceDouble, country = createCountry, city = createCity, membershipStatus = "Free Membership", assignedNid = createNid ) showCreateAgentDialog = false // clear createName = "" createPhone = "" createEmail = "" createNid = "" } }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier.weight(1.5f).testTag("admin_dialog_confirm_creation") ) { Text("Authorize Node", fontWeight = FontWeight.Bold) } } } } } } } // Funding Dialog Option if (selectedWalletToFund != null) { val target = selectedWalletToFund!! Dialog(onDismissRequest = { selectedWalletToFund = null }) { Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Column( modifier = Modifier.padding(20.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { Text( "Inject Escrow Funding", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = TextPrimary ) Text( "This will add BDT balance directly into ${target.name}'s Cash Agent register. This is supported by physical payment or system deposit checks.", style = MaterialTheme.typography.bodySmall, color = TextSecondary ) OutlinedTextField( value = fundingAmountInput, onValueChange = { fundingAmountInput = it }, label = { Text("Amount in BDT (৳)") }, singleLine = true, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary, focusedLabelColor = BrandGold ), modifier = Modifier.fillMaxWidth().testTag("fund_agent_amount_input") ) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { OutlinedButton( onClick = { selectedWalletToFund = null fundingAmountInput = "" }, modifier = Modifier.weight(1f) ) { Text("Cancel") } Button( onClick = { val amountValue = fundingAmountInput.toDoubleOrNull() ?: 0.0 if (amountValue > 0.0) { viewModel.fundAgentWallet(target.userId, amountValue) fundingAmountInput = "" selectedWalletToFund = null } }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier.weight(1f).testTag("fund_agent_dialog_confirm") ) { Text("Confirm Inject", fontWeight = FontWeight.Bold) } } } } } } }