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.ui.graphics.vector.ImageVector import androidx.compose.foundation.layout.* 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.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.Chat 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.Brush 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 androidx.compose.ui.window.DialogProperties import com.example.data.BookingEntity import com.example.data.ModelProfile import com.example.data.TransactionLog import com.example.ui.PortalViewModel import com.example.ui.theme.* import java.text.SimpleDateFormat import java.util.* @Composable fun ModelPortalView( viewModel: PortalViewModel, modelProfile: ModelProfile, bookings: List, logs: List ) { var selectedTab by remember { mutableStateOf("dashboard") } // "dashboard", "bookings", "earnings", "profile", "helpline" val modelBookings = bookings.filter { it.modelId == modelProfile.id } val modelLogs = logs.filter { it.accountId == "model_${modelProfile.id}" } Scaffold( bottomBar = { NavigationBar( containerColor = DarkGreyGlass, modifier = Modifier.windowInsetsPadding(WindowInsets.navigationBars).testTag("model_bottom_nav") ) { NavigationBarItem( selected = selectedTab == "dashboard", onClick = { selectedTab = "dashboard" }, icon = { Icon(Icons.Default.Dashboard, contentDescription = "Dashboard") }, label = { Text("Dashboard") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) NavigationBarItem( selected = selectedTab == "bookings", onClick = { selectedTab = "bookings" }, icon = { Icon(Icons.Default.Event, contentDescription = "Requests") }, label = { Text("Bookings") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) NavigationBarItem( selected = selectedTab == "earnings", onClick = { selectedTab = "earnings" }, icon = { Icon(Icons.Default.Payments, contentDescription = "Earnings") }, label = { Text("Earnings") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) NavigationBarItem( selected = selectedTab == "profile", onClick = { selectedTab = "profile" }, icon = { Icon(Icons.Default.PhotoLibrary, contentDescription = "Profile") }, label = { Text("Portfolio") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) NavigationBarItem( selected = selectedTab == "helpline", onClick = { selectedTab = "helpline" }, icon = { Icon(Icons.AutoMirrored.Filled.Chat, contentDescription = "Helpline") }, label = { Text("Helpline") }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandGold, unselectedIconColor = TextSecondary, selectedTextColor = BrandGold, indicatorColor = DarkGreyGlassElevated ) ) } }, containerColor = Obsidian ) { paddingValues -> Box( modifier = Modifier .fillMaxSize() .padding(paddingValues) ) { when (selectedTab) { "dashboard" -> ModelDashboardTab( viewModel = viewModel, model = modelProfile, bookings = modelBookings ) "bookings" -> ModelBookingsTab( viewModel = viewModel, bookings = modelBookings ) "earnings" -> ModelEarningsTab( viewModel = viewModel, model = modelProfile, logs = modelLogs ) "profile" -> ModelProfileTab( viewModel = viewModel, model = modelProfile ) "helpline" -> ContactHelplineTab( viewModel = viewModel, senderId = "model_${modelProfile.id}", senderName = modelProfile.name ) } } } } @Composable fun ModelDashboardTab( viewModel: PortalViewModel, model: ModelProfile, bookings: List ) { val pendingCount = bookings.filter { it.status == "PENDING" }.size val activeCount = bookings.filter { it.status == "ACCEPTED" }.size val completedCount = bookings.filter { it.status == "COMPLETED" }.size LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { item { Spacer(modifier = Modifier.height(12.dp)) // Profile Card Info Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Row( modifier = Modifier.padding(20.dp), verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier .size(72.dp) .clip(CircleShape) .background( Brush.linearGradient( colors = listOf(GoldGradientStart, GoldGradientEnd) ) ), contentAlignment = Alignment.Center ) { Text( model.name.first().toString(), fontWeight = FontWeight.Bold, color = Color.White, fontSize = 24.sp ) } Spacer(modifier = Modifier.width(16.dp)) Column(modifier = Modifier.weight(1f)) { Row(verticalAlignment = Alignment.CenterVertically) { Text(model.name, style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Spacer(modifier = Modifier.width(6.dp)) if (model.isVerified) { Icon(Icons.Default.Verified, "Verified", tint = CyanAccent, modifier = Modifier.size(16.dp)) } } Text("@${model.username} • Talent Portal", style = MaterialTheme.typography.bodySmall, color = BrandGold) Spacer(modifier = Modifier.height(4.dp)) Row(verticalAlignment = Alignment.CenterVertically) { Box( modifier = Modifier .size(10.dp) .background(if (model.isOnline) CyanAccent else Color.Gray, CircleShape) ) Spacer(modifier = Modifier.width(6.dp)) Text( if (model.isOnline) "Online & Accepting Bookings" else "Offline / Restricted", style = MaterialTheme.typography.bodySmall, color = TextSecondary ) } } } } } // Quick Online Status Switch & Availability setup item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated) ) { Row( modifier = Modifier .padding(16.dp) .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column { Text("Online Availability Status", style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Toggle your search visible state on directory.", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } Switch( checked = model.isOnline, onCheckedChange = { viewModel.updateModelOnlineStatus(it) }, colors = SwitchDefaults.colors( checkedThumbColor = Color.Black, checkedTrackColor = CyanAccent, uncheckedThumbColor = Color.Gray, uncheckedTrackColor = DarkGreyGlass ), modifier = Modifier.testTag("online_status_switch") ) } } } // Stats grid item { Row( horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth() ) { StatCard( modifier = Modifier.weight(1f), title = "Pending Slots", value = pendingCount.toString(), icon = Icons.Default.PendingActions, color = BrandGold ) StatCard( modifier = Modifier.weight(1f), title = "Active Booked", value = activeCount.toString(), icon = Icons.Default.Timer, color = CyanAccent ) StatCard( modifier = Modifier.weight(1f), title = "Completed", value = completedCount.toString(), icon = Icons.Default.CheckCircle, color = Color.Green ) } } // Earnings overview item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column(modifier = Modifier.padding(20.dp)) { Text("Total Cleared Earnings (Net 85%)", color = TextSecondary, style = MaterialTheme.typography.bodySmall) Text( String.format(Locale.US, "$%,.2f", model.totalEarnings), color = Color.Green, style = MaterialTheme.typography.headlineLarge, fontWeight = FontWeight.ExtraBold ) Spacer(modifier = Modifier.height(6.dp)) Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Text("Pending Escrow Clearing:", color = TextSecondary, style = MaterialTheme.typography.bodySmall) Text(String.format(Locale.US, "$%,.2f", model.pendingEarnings), color = BrandGold, fontWeight = FontWeight.Bold) } Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Text("Promotion Tier:", color = TextSecondary, style = MaterialTheme.typography.bodySmall) Text(model.membershipStatus, color = BrandGold, fontWeight = FontWeight.Bold) } } } } // Live notifications item { Text("Pending Requests Action Feed", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) } val pendingList = bookings.filter { it.status == "PENDING" } if (pendingList.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( "No pending reservation requests.", color = TextSecondary, modifier = Modifier .padding(24.dp) .fillMaxWidth(), textAlign = TextAlign.Center, style = MaterialTheme.typography.bodySmall ) } } } else { items(pendingList) { booking -> PendingActionCard(booking = booking, viewModel = viewModel) } } item { Spacer(modifier = Modifier.height(30.dp)) } } } @Composable fun PendingActionCard( booking: BookingEntity, viewModel: PortalViewModel ) { Card( modifier = Modifier .fillMaxWidth() .testTag("pending_action_card_${booking.id}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated) ) { Column(modifier = Modifier.padding(16.dp)) { Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Column { Text("Session Request #${booking.id}", color = BrandGold, style = MaterialTheme.typography.labelSmall, fontWeight = FontWeight.Bold) Text("Client: ${booking.userName}", color = TextPrimary, style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold) } Text( String.format(Locale.US, "$%,.0f", booking.totalPrice), color = TextPrimary, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.ExtraBold ) } Spacer(modifier = Modifier.height(6.dp)) Row { Icon(Icons.Default.Place, "loc", tint = BrandGold, modifier = Modifier.size(14.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Safehouse: ${booking.location}", color = TextSecondary, style = MaterialTheme.typography.bodySmall) } Row { Icon(Icons.Default.DateRange, "date", tint = BrandGold, modifier = Modifier.size(14.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Slot: ${booking.dateString} (${booking.durationHours} hrs)", color = TextSecondary, style = MaterialTheme.typography.bodySmall) } Spacer(modifier = Modifier.height(12.dp)) Row( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth() ) { OutlinedButton( onClick = { viewModel.cancelBooking(booking.id, isModelCancelling = true) }, colors = ButtonDefaults.outlinedButtonColors(contentColor = CoralRed), border = BorderStroke(1.dp, CoralRed.copy(alpha = 0.5f)), modifier = Modifier .weight(1f) .height(34.dp) .testTag("reject_btn_${booking.id}"), contentPadding = PaddingValues(0.dp) ) { Text("Cancel / Deny", fontSize = 12.sp) } Button( onClick = { viewModel.acceptBooking(booking.id) }, colors = ButtonDefaults.buttonColors(containerColor = CyanAccent, contentColor = Color.Black), modifier = Modifier .weight(1f) .height(34.dp) .testTag("accept_btn_${booking.id}"), contentPadding = PaddingValues(0.dp) ) { Text("Accept Booking", fontSize = 12.sp, fontWeight = FontWeight.Bold) } } } } } @Composable fun StatCard( modifier: Modifier = Modifier, title: String, value: String, icon: ImageVector, color: Color ) { Card( modifier = modifier, colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column( modifier = Modifier.padding(12.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Icon(icon, contentDescription = title, tint = color, modifier = Modifier.size(24.dp)) Spacer(modifier = Modifier.height(8.dp)) Text(value, style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, color = TextPrimary) Text(title, style = MaterialTheme.typography.labelSmall, color = TextSecondary, textAlign = TextAlign.Center) } } } @Composable fun SelfieUploadDialog( booking: BookingEntity, viewModel: PortalViewModel, onDismiss: () -> Unit ) { var selectedSelfie by remember { mutableStateOf("proof_front_profile.png") } var noteInput by remember { mutableStateOf("Service done, uploading selfie proof for verification.") } androidx.compose.material3.AlertDialog( onDismissRequest = onDismiss, title = { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.CameraAlt, contentDescription = null, tint = BrandGold) Spacer(modifier = Modifier.width(8.dp)) Text("Selfie Verification Upload", color = TextPrimary) } }, text = { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { Text( "To release the escrow-locked ৳${String.format(java.util.Locale.US, "%,.0f", booking.totalPrice)} BDT, please capture or upload a live verification selfie confirming safehouse service completion.", color = TextSecondary, style = MaterialTheme.typography.bodySmall ) Text( "Select Selfie Image Source:", color = TextPrimary, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.labelSmall ) // Render simulated options for the model val selfieOptions = listOf( "proof_safehouse_interior.png" to "Interior Safehouse Profile Biometric", "proof_holding_id.jpg" to "Model holding NID verification alignment", "proof_on_location.png" to "Real-time location coordinate match selfie" ) selfieOptions.forEach { (filename, label) -> val isSelected = selectedSelfie == filename Card( modifier = Modifier .fillMaxWidth() .clickable { selectedSelfie = filename } .testTag("selfie_option_${filename}"), colors = CardDefaults.cardColors( containerColor = if (isSelected) BrandGold.copy(0.15f) else DarkGreyGlass ), border = BorderStroke(1.dp, if (isSelected) BrandGold else DarkGreyGlassElevated) ) { Row( modifier = Modifier.padding(12.dp), verticalAlignment = Alignment.CenterVertically ) { RadioButton( selected = isSelected, onClick = { selectedSelfie = filename }, colors = RadioButtonDefaults.colors(selectedColor = BrandGold) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text(label, style = MaterialTheme.typography.bodySmall, color = TextPrimary, fontWeight = FontWeight.Bold) Text("File: $filename", style = MaterialTheme.typography.labelSmall, color = TextSecondary) } } } } OutlinedTextField( value = noteInput, onValueChange = { noteInput = it }, label = { Text("Completion Comments") }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlassElevated, unfocusedContainerColor = DarkGreyGlassElevated, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), modifier = Modifier.fillMaxWidth().testTag("selfie_comment_field") ) } }, confirmButton = { Button( onClick = { viewModel.submitProof(booking.id, booking.modelId, selectedSelfie) onDismiss() }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold), modifier = Modifier.testTag("submit_selfie_proof_btn") ) { Text("Submit Verification Proof", color = Color.White) } }, dismissButton = { TextButton( onClick = onDismiss, modifier = Modifier.testTag("dismiss_selfie_proof_btn") ) { Text("Cancel", color = TextSecondary) } }, containerColor = Obsidian, shape = RoundedCornerShape(16.dp), properties = DialogProperties(usePlatformDefaultWidth = false) ) } @Composable fun ModelBookingsTab( viewModel: PortalViewModel, bookings: List ) { var statusSelect by remember { mutableStateOf("ALL") } var activeChatBooking by remember { mutableStateOf(null) } var bookingForSelfieUpload by remember { mutableStateOf(null) } if (activeChatBooking != null) { Private1vs1ChatDialog( booking = activeChatBooking!!, viewModel = viewModel, senderType = "MODEL", onDismiss = { activeChatBooking = null } ) } if (bookingForSelfieUpload != null) { SelfieUploadDialog( booking = bookingForSelfieUpload!!, viewModel = viewModel, onDismiss = { bookingForSelfieUpload = null } ) } val filteredList = if (statusSelect == "ALL") bookings else bookings.filter { it.status == statusSelect } LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { item { Spacer(modifier = Modifier.height(12.dp)) Text("Client Sessions Ledger", style = MaterialTheme.typography.titleLarge, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Act on bookings, manage reservations, and confirm service completion.", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) Spacer(modifier = Modifier.height(8.dp)) } item { Row( horizontalArrangement = Arrangement.spacedBy(4.dp), modifier = Modifier.fillMaxWidth() ) { listOf("ALL", "PENDING", "ACCEPTED", "COMPLETED", "CANCELLED").forEach { item -> val isChecked = statusSelect == item Surface( modifier = Modifier .weight(1f) .clickable { statusSelect = item } .testTag("model_status_$item"), shape = RoundedCornerShape(8.dp), color = if (isChecked) BrandGold else DarkGreyGlass, border = BorderStroke(1.dp, if (isChecked) Color.Transparent else DarkGreyGlassElevated) ) { Text( text = item.substring(0, 3), modifier = Modifier.padding(vertical = 8.dp), textAlign = TextAlign.Center, color = if (isChecked) Color.Black else TextSecondary, style = MaterialTheme.typography.labelSmall, fontWeight = FontWeight.Bold ) } } } } if (filteredList.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth().padding(vertical = 40.dp), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( "No bookings found in this ledger.", color = TextSecondary, modifier = Modifier .padding(32.dp) .fillMaxWidth(), textAlign = TextAlign.Center, style = MaterialTheme.typography.bodySmall ) } } } else { items(filteredList) { booking -> Card( modifier = Modifier .fillMaxWidth() .testTag("model_booking_card_${booking.id}"), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column(modifier = Modifier.padding(16.dp)) { Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Column { Text("Booking Ref #${booking.id}", style = MaterialTheme.typography.labelSmall, color = TextSecondary) Text("User: ${booking.userName}", style = MaterialTheme.typography.titleSmall, color = TextPrimary, fontWeight = FontWeight.Bold) } val badgeColor = when (booking.status) { "PENDING" -> BrandGold "ACCEPTED" -> CyanAccent "PROOF_UPLOADED" -> LightGold "COMPLETED" -> Color.Green else -> CoralRed } Surface( shape = RoundedCornerShape(4.dp), color = badgeColor.copy(alpha = 0.15f), contentColor = badgeColor ) { Text( booking.status, modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp), style = MaterialTheme.typography.labelSmall, fontWeight = FontWeight.Bold ) } } HorizontalDivider(color = DarkGreyGlassElevated, modifier = Modifier.padding(vertical = 12.dp)) Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Column { Text("Location: ${booking.location}", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Text("Date / Hours: ${booking.dateString} (${booking.durationHours} hrs)", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } Text( String.format(Locale.US, "$%,.2f", booking.totalPrice), color = BrandGold, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyLarge ) } if (booking.status == "ACCEPTED") { Spacer(modifier = Modifier.height(12.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Button( onClick = { activeChatBooking = booking }, colors = ButtonDefaults.buttonColors(containerColor = CyanAccent, contentColor = Color.Black), modifier = Modifier .weight(1f) .height(36.dp) .testTag("chat_1vs1_model_btn_${booking.id}"), shape = RoundedCornerShape(8.dp) ) { Text("💬 1-on-1 Secured Chat", fontWeight = FontWeight.Bold, fontSize = 11.sp) } Button( onClick = { bookingForSelfieUpload = booking }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.White), modifier = Modifier .weight(1f) .height(36.dp) .testTag("upload_selfie_btn_${booking.id}"), shape = RoundedCornerShape(8.dp) ) { Text("📸 Upload Selfie Proof", fontWeight = FontWeight.Bold, fontSize = 11.sp) } } } if (booking.status == "PROOF_UPLOADED") { Spacer(modifier = Modifier.height(12.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Button( onClick = { activeChatBooking = booking }, colors = ButtonDefaults.buttonColors(containerColor = CyanAccent, contentColor = Color.Black), modifier = Modifier .weight(1f) .height(36.dp) .testTag("chat_1vs1_model_active_btn_${booking.id}"), shape = RoundedCornerShape(8.dp) ) { Text("💬 1-on-1 Secured Chat", fontWeight = FontWeight.Bold, fontSize = 11.sp) } Button( onClick = { /* disabled */ }, enabled = false, colors = ButtonDefaults.buttonColors( disabledContainerColor = DarkGreyGlassElevated, disabledContentColor = BrandGold.copy(0.6f) ), modifier = Modifier .weight(1f) .height(36.dp) .testTag("reviewing_selfie_btn_${booking.id}"), shape = RoundedCornerShape(8.dp) ) { Text("⏳ Reviewing Proof", fontWeight = FontWeight.Bold, fontSize = 11.sp) } } } } } } } item { Spacer(modifier = Modifier.height(30.dp)) } } } @Composable fun ModelEarningsTab( viewModel: PortalViewModel, model: ModelProfile, logs: List ) { var showWithdrawDialog by remember { mutableStateOf(false) } var withdrawInput by remember { mutableStateOf("") } var withdrawWalletName by remember { mutableStateOf("") } var withdrawWalletNumber by remember { mutableStateOf("") } var ledgerSuccessMsg by remember { mutableStateOf(null) } val myWithdrawals = viewModel.allWithdrawalsFlow.collectAsState(initial = emptyList()).value.filter { it.modelId == model.id } LazyColumn( modifier = Modifier .fillMaxSize() .padding(horizontal = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { item { Spacer(modifier = Modifier.height(12.dp)) Text("Earning Reports & Withdrawals", style = MaterialTheme.typography.titleLarge, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Request instant mobile balance payouts (bKash/Nagad/Rocket) via our trusted local agent networks.", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) Spacer(modifier = Modifier.height(12.dp)) } // Vault Display item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated) ) { Column(modifier = Modifier.padding(20.dp)) { Text("Available Cleared Earnings", color = TextSecondary, style = MaterialTheme.typography.labelSmall) Text( String.format(Locale.US, "৳%,.2f BDT", model.totalEarnings), color = Color.Green, style = MaterialTheme.typography.displaySmall, fontWeight = FontWeight.ExtraBold ) Spacer(modifier = Modifier.height(12.dp)) Button( onClick = { showWithdrawDialog = true }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(10.dp), modifier = Modifier .fillMaxWidth() .testTag("withdraw_btn") ) { Icon(Icons.Default.ArrowCircleDown, "withdraw") Spacer(modifier = Modifier.width(6.dp)) Text("Request Mobile Payout", fontWeight = FontWeight.Bold) } } } } // Active Withdrawal Tracking System if (myWithdrawals.isNotEmpty()) { item { Text("Your Payout Settlement Orders", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) } items(myWithdrawals) { wt -> Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass), border = BorderStroke(1.dp, if (wt.status == "PENDING") BrandGold.copy(0.3f) else Color.White.copy(0.04f)) ) { Column(modifier = Modifier.padding(14.dp), verticalArrangement = Arrangement.spacedBy(6.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text("Payout BDT: ৳${wt.amount}", fontWeight = FontWeight.Bold, color = TextPrimary) Surface( shape = RoundedCornerShape(4.dp), color = when (wt.status) { "CONFIRMED" -> Color.Green.copy(0.12f) "SENT" -> Color.Cyan.copy(0.12f) "PENDING" -> BrandGold.copy(0.12f) else -> CoralRed.copy(0.12f) }, contentColor = when (wt.status) { "CONFIRMED" -> Color.Green "SENT" -> Color.Cyan "PENDING" -> BrandGold else -> CoralRed } ) { Text( text = wt.status.uppercase(), style = MaterialTheme.typography.labelSmall, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp) ) } } Text("Acc Name: ${wt.name}", fontSize = 11.sp, color = TextSecondary) Text("Wallet Number: ${wt.walletNumber}", fontSize = 11.sp, color = TextPrimary) if (wt.agentName.isNotEmpty()) { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.VerifiedUser, "agent", tint = BrandGold, modifier = Modifier.size(12.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Handler Node: Agent ${wt.agentName}", fontSize = 10.sp, color = BrandGold) } } if (wt.screenshot.isNotEmpty()) { Row( modifier = Modifier .fillMaxWidth() .background(Color.White.copy(0.04f), RoundedCornerShape(4.dp)) .padding(horizontal = 8.dp, vertical = 4.dp), verticalAlignment = Alignment.CenterVertically ) { Icon(Icons.Default.Image, "payout_img", tint = Color.Green, modifier = Modifier.size(12.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Receipt Uploaded: ${wt.screenshot}", fontSize = 10.sp, color = Color.Green) } } } } } } item { Text("Session Compensation History", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) } if (logs.isEmpty()) { item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Text( "No withdrawal logs found.", color = TextSecondary, modifier = Modifier .padding(24.dp) .fillMaxWidth(), textAlign = TextAlign.Center ) } } } else { items(logs) { log -> LedgerItemCard(log = log) } } item { Spacer(modifier = Modifier.height(30.dp)) } } if (showWithdrawDialog) { Dialog(onDismissRequest = { showWithdrawDialog = false }) { Card( colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), shape = RoundedCornerShape(20.dp), modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Column(modifier = Modifier.padding(24.dp), verticalArrangement = Arrangement.spacedBy(12.dp)) { Icon(Icons.Default.LocalAtm, "Atm", tint = BrandGold, modifier = Modifier.size(48.dp)) Text("Request Mobile Payout", style = MaterialTheme.typography.titleLarge, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Cash Agent node will manually verify and fulfill your payout via bKash/Nagad/Rocket networks.", style = MaterialTheme.typography.bodySmall, color = TextSecondary) OutlinedTextField( value = withdrawInput, onValueChange = { withdrawInput = it }, label = { Text("Payout Amount (৳ BDT)") }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary, focusedLabelColor = BrandGold ), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true, modifier = Modifier.fillMaxWidth().testTag("withdraw_input_field") ) OutlinedTextField( value = withdrawWalletName, onValueChange = { withdrawWalletName = it }, label = { Text("Mobile wallet account name") }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary, focusedLabelColor = BrandGold ), singleLine = true, modifier = Modifier.fillMaxWidth().testTag("withdraw_wallet_name_field") ) OutlinedTextField( value = withdrawWalletNumber, onValueChange = { withdrawWalletNumber = it }, label = { Text("Wallet Phone Number (bKash/Nagad)") }, colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlass, unfocusedContainerColor = DarkGreyGlass, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary, focusedLabelColor = BrandGold ), singleLine = true, modifier = Modifier.fillMaxWidth().testTag("withdraw_wallet_number_field") ) Spacer(modifier = Modifier.height(8.dp)) Row( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth() ) { OutlinedButton( onClick = { showWithdrawDialog = false }, modifier = Modifier.weight(1f) ) { Text("Cancel") } val parsedAmt = withdrawInput.toDoubleOrNull() ?: 0.0 val validationSuccess = parsedAmt > 0.0 && withdrawWalletName.isNotEmpty() && withdrawWalletNumber.isNotEmpty() Button( onClick = { viewModel.submitWithdrawalRequest( amount = parsedAmt, name = withdrawWalletName, walletNumber = withdrawWalletNumber ) { success, msg -> ledgerSuccessMsg = msg if (success) { showWithdrawDialog = false withdrawInput = "" withdrawWalletName = "" withdrawWalletNumber = "" } } }, enabled = validationSuccess, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier .weight(1f) .testTag("withdraw_confirm_btn") ) { Text("Confirm", fontWeight = FontWeight.Bold) } } } } } } ledgerSuccessMsg?.let { msg -> AlertDialog( onDismissRequest = { ledgerSuccessMsg = null }, title = { Text("Payout Settlement system", fontWeight = FontWeight.Bold) }, text = { Text(msg) }, confirmButton = { TextButton(onClick = { ledgerSuccessMsg = null }) { Text("OK", color = BrandGold) } }, containerColor = DarkGreyGlassElevated, textContentColor = TextPrimary, titleContentColor = TextPrimary ) } } @Composable fun ModelProfileTab( viewModel: PortalViewModel, model: ModelProfile ) { var editBio by remember { mutableStateOf(model.bio) } var editLocation by remember { mutableStateOf(model.location) } var editRate by remember { mutableStateOf(model.hourlyRate.toString()) } var alertMessage 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("Professional Portfolio Hub", style = MaterialTheme.typography.titleLarge, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Set availability safehouses, customise booking rates, and upload composite cards.", style = MaterialTheme.typography.bodyMedium, color = TextSecondary) Spacer(modifier = Modifier.height(12.dp)) } // 🔐 MODEL ACCOUNT VERIFICATION SYSTEM CARD item { var showEmailWizard by remember { mutableStateOf(false) } var showPhoneWizard by remember { mutableStateOf(false) } var isSmsSent by remember { mutableStateOf(false) } var smsOtpInput by remember { mutableStateOf("") } var kycNidInput by remember { mutableStateOf("") } var kycSelfieInput by remember { mutableStateOf("https://images.unsplash.com/photo-1544005313-94ddf0286df2?auto=format&fit=crop&w=400&q=80") } var firebaseVerificationId by remember { mutableStateOf("") } var tempPhoneInput by remember { mutableStateOf(model.phone.ifEmpty { "+880 1812-998877" }) } var tempEmailInput by remember { mutableStateOf(model.email.ifEmpty { "model@example.com" }) } var isVerifyingFirebase by remember { mutableStateOf(false) } var firebaseStatusMessage by remember { mutableStateOf("") } val firebaseLogs by viewModel.firebaseLogs.collectAsState(initial = emptyList()) val context = androidx.compose.ui.platform.LocalContext.current val activity = context as? android.app.Activity Card( colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated), modifier = Modifier.fillMaxWidth().testTag("model_verification_card"), shape = RoundedCornerShape(16.dp), border = BorderStroke(1.dp, if (model.verificationStatus == "verified") Color.Green.copy(0.3f) else BrandGold.copy(0.2f)) ) { Column(modifier = Modifier.padding(18.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column { Text( text = "Account Verification (Required)", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, color = TextPrimary ) Text( text = "Verified status is mandatory to receive secure payments.", style = MaterialTheme.typography.bodySmall, color = TextSecondary ) } // Badge representation val (statusText, badgeColor, badgeBg) = when (model.verificationStatus) { "verified" -> Triple("VERIFIED MODEL", Color.Green, Color.Green.copy(0.12f)) "pending" -> Triple("PENDING AUDIT", BrandGold, BrandGold.copy(0.12f)) "rejected" -> Triple("REJECTED / RETRY", CoralRed, CoralRed.copy(0.12f)) else -> Triple("UNVERIFIED", TextSecondary, DarkGreyGlass) } Surface( shape = RoundedCornerShape(6.dp), color = badgeBg, contentColor = badgeColor ) { Text( text = statusText, style = MaterialTheme.typography.labelSmall, modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp), fontWeight = FontWeight.ExtraBold ) } } Spacer(modifier = Modifier.height(16.dp)) HorizontalDivider(color = TextSecondary.copy(0.15f)) Spacer(modifier = Modifier.height(16.dp)) // 1. Email Verification Channel Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Row(verticalAlignment = Alignment.CenterVertically) { Icon( imageVector = if (model.emailVerified) Icons.Default.MarkEmailRead else Icons.Default.MailOutline, contentDescription = "Email verification", tint = if (model.emailVerified) Color.Green else TextSecondary, modifier = Modifier.size(20.dp) ) Spacer(modifier = Modifier.width(10.dp)) Column { Text("Email Verification", style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Text(model.email, style = MaterialTheme.typography.bodySmall, color = TextSecondary) } } if (model.emailVerified) { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.CheckCircle, "verified", tint = Color.Green, modifier = Modifier.size(16.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Verified", style = MaterialTheme.typography.bodySmall, color = Color.Green, fontWeight = FontWeight.Bold) } } else { Button( onClick = { showEmailWizard = !showEmailWizard }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(6.dp), modifier = Modifier.height(28.dp).testTag("model_verify_email_trigger"), contentPadding = PaddingValues(horizontal = 12.dp, vertical = 0.dp) ) { Text("Verify", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } if (!model.emailVerified && showEmailWizard) { Card( colors = CardDefaults.cardColors(containerColor = DarkGreyGlass), modifier = Modifier.fillMaxWidth().padding(top = 10.dp), shape = RoundedCornerShape(8.dp) ) { Column(modifier = Modifier.padding(12.dp)) { Text( "Enter email for Firebase Verification link dispatch:", style = MaterialTheme.typography.bodySmall, color = TextSecondary ) Spacer(modifier = Modifier.height(6.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically ) { TextField( value = tempEmailInput, onValueChange = { tempEmailInput = it }, colors = TextFieldDefaults.colors(focusedContainerColor = DarkGreyGlassElevated, unfocusedContainerColor = DarkGreyGlassElevated), singleLine = true, modifier = Modifier.weight(1f), textStyle = MaterialTheme.typography.bodyMedium.copy(color = TextPrimary) ) Button( onClick = { isVerifyingFirebase = true viewModel.sendFirebaseEmailVerification(tempEmailInput, isModel = true) { success, msg -> isVerifyingFirebase = false firebaseStatusMessage = msg if (success) { showEmailWizard = false } } }, enabled = !isVerifyingFirebase, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(6.dp), modifier = Modifier.height(44.dp).testTag("model_firebase_email_trigger") ) { if (isVerifyingFirebase) { CircularProgressIndicator(modifier = Modifier.size(16.dp), color = Color.Black, strokeWidth = 2.dp) } else { Text("Send Link", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } if (firebaseStatusMessage.isNotEmpty()) { Spacer(modifier = Modifier.height(4.dp)) Text(firebaseStatusMessage, style = MaterialTheme.typography.bodySmall, color = BrandGold) } } } } Spacer(modifier = Modifier.height(14.dp)) // 2. Phone Verification Channel Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Row(verticalAlignment = Alignment.CenterVertically) { Icon( imageVector = if (model.phoneVerified) Icons.Default.PhoneIphone else Icons.Default.PhonelinkRing, contentDescription = "Phone verification", tint = if (model.phoneVerified) Color.Green else TextSecondary, modifier = Modifier.size(20.dp) ) Spacer(modifier = Modifier.width(10.dp)) Column { Text("Mobile Phone OTP Verification", style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Text( text = if (model.phoneVerified) model.phone else "Verify phone to receive dispatch SMS notifications.", style = MaterialTheme.typography.bodySmall, color = TextSecondary ) } } if (model.phoneVerified) { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.CheckCircle, "verified", tint = Color.Green, modifier = Modifier.size(16.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Verified", style = MaterialTheme.typography.bodySmall, color = Color.Green, fontWeight = FontWeight.Bold) } } else { Button( onClick = { showPhoneWizard = !showPhoneWizard }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(6.dp), modifier = Modifier.height(28.dp).testTag("model_verify_phone_trigger"), contentPadding = PaddingValues(horizontal = 12.dp, vertical = 0.dp) ) { Text("Verify", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } if (!model.phoneVerified && showPhoneWizard) { Card( colors = CardDefaults.cardColors(containerColor = DarkGreyGlass), modifier = Modifier.fillMaxWidth().padding(top = 10.dp), shape = RoundedCornerShape(8.dp) ) { Column(modifier = Modifier.padding(12.dp)) { if (!isSmsSent) { Text("Enter model phone number to receive SMS OTP code:", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Spacer(modifier = Modifier.height(6.dp)) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) { TextField( value = tempPhoneInput, onValueChange = { tempPhoneInput = it }, colors = TextFieldDefaults.colors(focusedContainerColor = DarkGreyGlassElevated, unfocusedContainerColor = DarkGreyGlassElevated), singleLine = true, modifier = Modifier.weight(1f), textStyle = MaterialTheme.typography.bodyMedium.copy(color = TextPrimary) ) Button( onClick = { if (activity != null) { isVerifyingFirebase = true viewModel.startFirebasePhoneVerification( activity = activity, phoneNum = tempPhoneInput, onCodeSent = { verificationId -> isVerifyingFirebase = false firebaseVerificationId = verificationId isSmsSent = true firebaseStatusMessage = "Firebase SMS verification code sent! Input code below." }, onVerificationComplete = { success, msg -> isVerifyingFirebase = false firebaseStatusMessage = msg } ) } }, enabled = !isVerifyingFirebase, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(6.dp), modifier = Modifier.height(44.dp).testTag("model_sms_send_otp") ) { if (isVerifyingFirebase) { CircularProgressIndicator(modifier = Modifier.size(16.dp), color = Color.Black, strokeWidth = 2.dp) } else { Text("Send OTP", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } } else { Text("Verification Code sent! (Simulation code is 1234):", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Spacer(modifier = Modifier.height(6.dp)) Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) { TextField( value = smsOtpInput, onValueChange = { smsOtpInput = it }, colors = TextFieldDefaults.colors(focusedContainerColor = DarkGreyGlassElevated, unfocusedContainerColor = DarkGreyGlassElevated), singleLine = true, placeholder = { Text("Code") }, modifier = Modifier.weight(1f), textStyle = MaterialTheme.typography.bodyMedium.copy(color = TextPrimary), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number) ) Button( onClick = { isVerifyingFirebase = true viewModel.submitFirebaseOtp( verificationId = firebaseVerificationId, code = smsOtpInput, isModel = true, phoneNum = tempPhoneInput ) { success, msg -> isVerifyingFirebase = false firebaseStatusMessage = msg if (success) { showPhoneWizard = false isSmsSent = false smsOtpInput = "" } } }, enabled = !isVerifyingFirebase, colors = ButtonDefaults.buttonColors(containerColor = Color.Green, contentColor = Color.Black), shape = RoundedCornerShape(6.dp), modifier = Modifier.height(44.dp).testTag("model_sms_otp_submit") ) { if (isVerifyingFirebase) { CircularProgressIndicator(modifier = Modifier.size(16.dp), color = Color.Black, strokeWidth = 2.dp) } else { Text("Submit OTP", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } } if (firebaseStatusMessage.isNotEmpty()) { Spacer(modifier = Modifier.height(4.dp)) Text(firebaseStatusMessage, style = MaterialTheme.typography.bodySmall, color = BrandGold) } } } } // Bottom Drawer Console representing active trace if (showEmailWizard || showPhoneWizard) { Spacer(modifier = Modifier.height(10.dp)) FirebaseConsoleCard( logs = firebaseLogs, onClearLogs = { viewModel.clearFirebaseLogs() } ) } Spacer(modifier = Modifier.height(14.dp)) // 3. Model Identity Verification (KYC Verification) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Row(verticalAlignment = Alignment.CenterVertically) { Icon( imageVector = if (model.kycStatus == "verified") Icons.Default.Badge else Icons.Default.AssignmentInd, contentDescription = "KYC status", tint = if (model.kycStatus == "verified") Color.Green else if (model.kycStatus == "rejected") CoralRed else if (model.kycStatus == "pending") BrandGold else TextSecondary, modifier = Modifier.size(20.dp) ) Spacer(modifier = Modifier.width(10.dp)) Column { Text("NID / Identity Verification (KYC)", style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.Bold) val subText = when (model.kycStatus) { "verified" -> "Approved ID Card: ${model.nidNumber}" "pending" -> "Escrow and administration team audits are active..." "rejected" -> "Documents rejected. Please re-submit verification cards." else -> "Requires National ID document & live facial selfie comparison." } Text(subText, style = MaterialTheme.typography.bodySmall, color = TextSecondary) } } if (model.kycStatus == "verified") { Row(verticalAlignment = Alignment.CenterVertically) { Icon(Icons.Default.CheckCircle, "verified", tint = Color.Green, modifier = Modifier.size(16.dp)) Spacer(modifier = Modifier.width(4.dp)) Text("Approved", style = MaterialTheme.typography.bodySmall, color = Color.Green, fontWeight = FontWeight.Bold) } } else if (model.kycStatus == "pending") { Row(verticalAlignment = Alignment.CenterVertically) { CircularProgressIndicator(color = BrandGold, modifier = Modifier.size(14.dp), strokeWidth = 2.dp) Spacer(modifier = Modifier.width(4.dp)) Text("Pending", style = MaterialTheme.typography.bodySmall, color = BrandGold, fontWeight = FontWeight.Medium) } } else { Row(verticalAlignment = Alignment.CenterVertically) { if (model.kycStatus == "rejected") { Text("Rejected", style = MaterialTheme.typography.bodySmall, color = CoralRed, fontWeight = FontWeight.Bold, modifier = Modifier.padding(end = 8.dp)) } val kycButtonLabel = if (model.kycStatus == "rejected") "Re-verify" else "Verify" Button( onClick = { if (kycNidInput.isEmpty()) { kycNidInput = "661259837" } viewModel.submitModelKYC(kycNidInput, kycSelfieInput) }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), shape = RoundedCornerShape(6.dp), modifier = Modifier.height(28.dp).testTag("model_submit_kyc_btn"), contentPadding = PaddingValues(horizontal = 12.dp, vertical = 0.dp) ) { Text(kycButtonLabel, fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } } if (model.kycStatus != "verified" && model.kycStatus != "pending") { Card( colors = CardDefaults.cardColors(containerColor = DarkGreyGlass), modifier = Modifier.fillMaxWidth().padding(top = 12.dp) ) { Column(modifier = Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Text("KYC Document Upload Simulator", style = MaterialTheme.typography.labelSmall, color = BrandGold, fontWeight = FontWeight.Bold) TextField( value = kycNidInput, onValueChange = { kycNidInput = it }, label = { Text("National ID / Passport Number") }, colors = TextFieldDefaults.colors(focusedContainerColor = DarkGreyGlassElevated, unfocusedContainerColor = DarkGreyGlassElevated), singleLine = true, modifier = Modifier.fillMaxWidth() ) TextField( value = kycSelfieInput, onValueChange = { kycSelfieInput = it }, label = { Text("Live Photo Face Selfie URL") }, colors = TextFieldDefaults.colors(focusedContainerColor = DarkGreyGlassElevated, unfocusedContainerColor = DarkGreyGlassElevated), singleLine = true, modifier = Modifier.fillMaxWidth() ) Text("Selfie preview simulation: [Automatic high-fidelity camera feed connected]", style = MaterialTheme.typography.bodySmall, color = TextSecondary) } } } } } } // Profile Form Setup item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column(modifier = Modifier.padding(16.dp)) { Text("Configure Rates & Locations", style = MaterialTheme.typography.titleSmall, color = BrandGold, fontWeight = FontWeight.Bold) Spacer(modifier = Modifier.height(12.dp)) Text("Hourly Booking Fee ($ USD/hr)", style = MaterialTheme.typography.bodySmall, color = TextSecondary) TextField( value = editRate, onValueChange = { editRate = it }, modifier = Modifier .fillMaxWidth() .testTag("model_rate_input"), colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlassElevated, unfocusedContainerColor = DarkGreyGlassElevated, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true ) Spacer(modifier = Modifier.height(10.dp)) Text("Active Booking Safehouse/Venue", style = MaterialTheme.typography.bodySmall, color = TextSecondary) TextField( value = editLocation, onValueChange = { editLocation = it }, modifier = Modifier .fillMaxWidth() .testTag("model_location_input"), colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlassElevated, unfocusedContainerColor = DarkGreyGlassElevated, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ), singleLine = true ) Spacer(modifier = Modifier.height(10.dp)) Text("About Portfolio Biography", style = MaterialTheme.typography.bodySmall, color = TextSecondary) TextField( value = editBio, onValueChange = { editBio = it }, modifier = Modifier .fillMaxWidth() .testTag("model_bio_input"), colors = TextFieldDefaults.colors( focusedContainerColor = DarkGreyGlassElevated, unfocusedContainerColor = DarkGreyGlassElevated, focusedTextColor = TextPrimary, unfocusedTextColor = TextPrimary ) ) Spacer(modifier = Modifier.height(16.dp)) Button( onClick = { val rInt = editRate.toDoubleOrNull() ?: model.hourlyRate viewModel.updateModelPricing(rInt) viewModel.updateModelDetails(editBio, editLocation) alertMessage = "Portfolio metadata updated successfully!" }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier .fillMaxWidth() .testTag("model_save_portfolio_btn") ) { Text("Save Portfolio Card", fontWeight = FontWeight.Bold) } } } } // Tier levels Promo cards item { Text("Profile Promotion & Premium Visibility", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) } item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column(modifier = Modifier.padding(16.dp)) { Text("Model VIP Plan", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Promote your composite card to priority search headers for users. Direct notification pings.", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Spacer(modifier = Modifier.height(12.dp)) Button( onClick = { viewModel.upgradeModelMembership("VIP Plan") }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier .fillMaxWidth() .testTag("model_upgrade_vip_btn") ) { Text("Activate VIP Profile", fontWeight = FontWeight.Bold) } } } } item { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass) ) { Column(modifier = Modifier.padding(16.dp)) { Text("Model Premium Plan", style = MaterialTheme.typography.titleMedium, color = TextPrimary, fontWeight = FontWeight.Bold) Text("Ultimate golden agency badge wrapper. Zero commission on safehouse booking revenues. Featured slideshow spotlight.", style = MaterialTheme.typography.bodySmall, color = TextSecondary) Spacer(modifier = Modifier.height(12.dp)) Button( onClick = { viewModel.upgradeModelMembership("Premium Plan") }, colors = ButtonDefaults.buttonColors(containerColor = BrandGold, contentColor = Color.Black), modifier = Modifier .fillMaxWidth() .testTag("model_upgrade_premium_btn") ) { Text("Activate Premium Spotlight", fontWeight = FontWeight.Bold) } } } } item { Spacer(modifier = Modifier.height(12.dp)) Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlassElevated) ) { Column(modifier = Modifier.padding(16.dp)) { Text("Reset simulation credentials", color = TextPrimary, style = MaterialTheme.typography.bodyMedium) Spacer(modifier = Modifier.height(8.dp)) Button( onClick = { viewModel.modelLogout() }, colors = ButtonDefaults.buttonColors(containerColor = CoralRed, contentColor = Color.White), modifier = Modifier .fillMaxWidth() .testTag("model_logout_btn") ) { Text("Logout / Choose Different Model") } } } } item { Spacer(modifier = Modifier.height(30.dp)) } } alertMessage?.let { msg -> AlertDialog( onDismissRequest = { alertMessage = null }, title = { Text("Agency Portfolio Center", fontWeight = FontWeight.Bold) }, text = { Text(msg) }, confirmButton = { TextButton(onClick = { alertMessage = null }) { Text("OK", color = BrandGold) } }, containerColor = DarkGreyGlassElevated, textContentColor = TextPrimary, titleContentColor = TextPrimary ) } } @Composable fun LedgerItemCard(log: TransactionLog) { Card( modifier = Modifier.fillMaxWidth(), colors = CardDefaults.cardColors(containerColor = DarkGreyGlass), shape = RoundedCornerShape(12.dp) ) { Row( modifier = Modifier.padding(14.dp), verticalAlignment = Alignment.CenterVertically ) { Box( modifier = Modifier .size(36.dp) .clip(CircleShape) .background(Color.Green.copy(alpha = 0.12f)), contentAlignment = Alignment.Center ) { Icon( Icons.Default.ArrowUpward, "dir", tint = Color.Green, modifier = Modifier.size(16.dp) ) } Spacer(modifier = Modifier.width(14.dp)) Column(modifier = Modifier.weight(1f)) { Text(log.type, style = MaterialTheme.typography.labelSmall, color = BrandGold, fontWeight = FontWeight.ExtraBold) Text(log.description, style = MaterialTheme.typography.bodyMedium, color = TextPrimary, fontWeight = FontWeight.Medium) } Spacer(modifier = Modifier.width(8.dp)) Text( text = String.format(Locale.US, "$%,.2f", log.amount), color = if (log.amount > 0) Color.Green else CoralRed, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.bodyMedium ) } } }