package com.example.ui.components import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* 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.material.icons.outlined.* import androidx.compose.material3.* import androidx.compose.runtime.Composable 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.graphics.vector.ImageVector 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.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.data.model.* import com.example.ui.theme.* import com.example.ui.util.Localization @Composable fun AppHeader( title: String, subtitle: String? = null, currentLang: AppLanguage, onToggleLang: () -> Unit, onOpenNotifications: () -> Unit, unreadNotifCount: Int = 0, showBackButton: Boolean = false, onBack: () -> Unit = {}, isAdminMode: Boolean = false, modifier: Modifier = Modifier ) { Surface( color = MaterialTheme.colorScheme.surface, contentColor = LightTextPrimary, border = androidx.compose.foundation.BorderStroke(1.dp, LightOutline), shadowElevation = 1.dp, modifier = modifier.fillMaxWidth() ) { Column( modifier = Modifier .fillMaxWidth() .statusBarsPadding() .padding(horizontal = 16.dp, vertical = 12.dp) ) { Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f) ) { if (showBackButton) { IconButton( onClick = onBack, modifier = Modifier .testTag("btn_header_back") .size(40.dp) .clip(CircleShape) .background(LightSurfaceVariant) .border(1.dp, LightOutline, CircleShape) ) { Icon( imageVector = Icons.Default.ArrowBack, contentDescription = "Back", tint = LightTextPrimary, modifier = Modifier.size(20.dp) ) } Spacer(modifier = Modifier.width(10.dp)) } Column { // Uppercase tracking sub-label tag Text( text = if (currentLang == AppLanguage.BN) "সুরক্ষাকারী" else "SUROKKHA BD", style = MaterialTheme.typography.labelSmall.copy( color = BrandNavyLight, fontWeight = FontWeight.ExtraBold, letterSpacing = 1.2.sp, fontSize = 10.sp ) ) Row(verticalAlignment = Alignment.CenterVertically) { Text( text = title, style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.ExtraBold, fontSize = 19.sp, color = LightTextPrimary, letterSpacing = (-0.3).sp ), maxLines = 1, overflow = TextOverflow.Ellipsis ) if (isAdminMode) { Spacer(modifier = Modifier.width(6.dp)) Surface( color = StatusAmberBg, shape = RoundedCornerShape(6.dp), border = androidx.compose.foundation.BorderStroke(1.dp, StatusAmber.copy(alpha = 0.3f)), modifier = Modifier.padding(2.dp) ) { Text( text = "ADMIN", style = MaterialTheme.typography.labelSmall.copy( color = StatusAmber, fontWeight = FontWeight.Black, fontSize = 9.sp ), modifier = Modifier.padding(horizontal = 5.dp, vertical = 2.dp) ) } } } } } Row(verticalAlignment = Alignment.CenterVertically) { // Language Switcher Pill Surface( onClick = onToggleLang, shape = RoundedCornerShape(12.dp), color = LightSurfaceVariant, border = androidx.compose.foundation.BorderStroke(1.dp, LightOutline), modifier = Modifier .testTag("btn_toggle_language") .height(36.dp) ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 10.dp) ) { Icon( imageVector = Icons.Default.Language, contentDescription = "Language", tint = BrandBlueAccent, modifier = Modifier.size(15.dp) ) Spacer(modifier = Modifier.width(4.dp)) Text( text = if (currentLang == AppLanguage.BN) "English" else "বাংলা", style = MaterialTheme.typography.labelMedium.copy( fontWeight = FontWeight.Bold, color = LightTextPrimary, fontSize = 11.sp ) ) } } Spacer(modifier = Modifier.width(8.dp)) // Notifications Bell (Circular slate button with border) Surface( onClick = onOpenNotifications, shape = CircleShape, color = LightSurfaceVariant, border = androidx.compose.foundation.BorderStroke(1.dp, LightOutline), modifier = Modifier .size(38.dp) .testTag("btn_notifications") ) { Box( contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize() ) { Icon( imageVector = Icons.Outlined.Notifications, contentDescription = "Notifications", tint = LightTextPrimary, modifier = Modifier.size(20.dp) ) if (unreadNotifCount > 0) { Box( modifier = Modifier .align(Alignment.TopEnd) .padding(6.dp) .size(8.dp) .background(StatusWarningRed, CircleShape) .border(1.dp, Color.White, CircleShape) ) } } } Spacer(modifier = Modifier.width(8.dp)) // Avatar Circle Badge Box( modifier = Modifier .size(38.dp) .clip(CircleShape) .background(BrandBlueAccent), contentAlignment = Alignment.Center ) { Text( text = "BD", color = Color.White, style = MaterialTheme.typography.labelMedium.copy( fontWeight = FontWeight.ExtraBold, fontSize = 12.sp ) ) } } } } } } data class SatarkNavItem( val tab: BottomTab, val label: String, val selectedIcon: ImageVector, val unselectedIcon: ImageVector ) @Composable fun BottomNavBar( selectedTab: BottomTab, onTabSelected: (BottomTab) -> Unit, lang: AppLanguage, modifier: Modifier = Modifier ) { val items = listOf( SatarkNavItem( tab = BottomTab.HOME, label = if (lang == AppLanguage.BN) "হোম" else "Home", selectedIcon = Icons.Filled.Home, unselectedIcon = Icons.Outlined.Home ), SatarkNavItem( tab = BottomTab.SEARCH, label = if (lang == AppLanguage.BN) "যাচাই" else "Search", selectedIcon = Icons.Filled.Search, unselectedIcon = Icons.Outlined.Search ), SatarkNavItem( tab = BottomTab.REPORT, label = if (lang == AppLanguage.BN) "রিপোর্ট" else "Report", selectedIcon = Icons.Filled.AddAlert, unselectedIcon = Icons.Outlined.AddAlert ), SatarkNavItem( tab = BottomTab.ALERTS, label = if (lang == AppLanguage.BN) "সতর্কতা" else "Alerts", selectedIcon = Icons.Filled.Warning, unselectedIcon = Icons.Outlined.WarningAmber ), SatarkNavItem( tab = BottomTab.PROFILE, label = if (lang == AppLanguage.BN) "প্রোফাইল" else "Profile", selectedIcon = Icons.Filled.Person, unselectedIcon = Icons.Outlined.Person ) ) Surface( color = MaterialTheme.colorScheme.surface, border = androidx.compose.foundation.BorderStroke(1.dp, LightOutline), shadowElevation = 8.dp, modifier = modifier.fillMaxWidth() ) { NavigationBar( containerColor = Color.Transparent, tonalElevation = 0.dp, windowInsets = WindowInsets.navigationBars, modifier = Modifier .fillMaxWidth() .testTag("bottom_navigation_bar") ) { items.forEach { item -> val isSelected = selectedTab == item.tab NavigationBarItem( selected = isSelected, onClick = { onTabSelected(item.tab) }, icon = { Icon( imageVector = if (isSelected) item.selectedIcon else item.unselectedIcon, contentDescription = item.label, modifier = Modifier.size(22.dp) ) }, label = { Text( text = item.label, style = MaterialTheme.typography.labelSmall.copy( fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Medium, fontSize = 10.sp, letterSpacing = 0.2.sp ) ) }, colors = NavigationBarItemDefaults.colors( selectedIconColor = BrandBlueAccent, selectedTextColor = BrandBlueAccent, indicatorColor = BrandBlueLight, unselectedIconColor = LightTextMuted, unselectedTextColor = LightTextMuted ), modifier = Modifier.testTag("nav_tab_${item.tab.name.lowercase()}") ) } } } } @Composable fun VerificationBadge( status: VerificationStatus, lang: AppLanguage, modifier: Modifier = Modifier ) { val (bgColor, textColor, icon) = when (status) { VerificationStatus.VERIFIED_SAFE -> Triple(StatusVerifiedGreenLight, StatusVerifiedGreen, Icons.Default.CheckCircle) VerificationStatus.UNDER_REVIEW -> Triple(StatusReviewBlueBg, StatusReviewBlue, Icons.Default.HourglassTop) VerificationStatus.HIGH_RISK -> Triple(StatusWarningRedLight, StatusWarningRed, Icons.Default.Warning) VerificationStatus.BLACKLISTED_SCAM -> Triple(StatusWarningRedBg, StatusWarningRed, Icons.Default.Cancel) } Surface( color = bgColor, shape = RoundedCornerShape(50), border = androidx.compose.foundation.BorderStroke(1.dp, textColor.copy(alpha = 0.2f)), modifier = modifier ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp) ) { Icon( imageVector = icon, contentDescription = null, tint = textColor, modifier = Modifier.size(12.dp) ) Spacer(modifier = Modifier.width(4.dp)) Text( text = Localization.getVerificationStatusText(status, lang).uppercase(), style = MaterialTheme.typography.labelSmall.copy( color = textColor, fontWeight = FontWeight.ExtraBold, fontSize = 9.sp, letterSpacing = 0.5.sp ) ) } } } @Composable fun RiskScoreMeter( score: Int, lang: AppLanguage, modifier: Modifier = Modifier ) { val (meterColor, meterLabelEn, meterLabelBn) = when { score < 20 -> Triple(StatusVerifiedGreen, "Low Risk (Trustworthy)", "কম ঝুঁকি (নির্ভরযোগ্য)") score < 60 -> Triple(StatusAmber, "Moderate Risk (Caution)", "মাঝারি ঝুঁকি (সতর্ক থাকুন)") else -> Triple(StatusWarningRed, "High Scam Risk (Danger)", "উচ্চ ঝুঁকি (প্রতারণার আশঙ্কা)") } Surface( color = meterColor.copy(alpha = 0.1f), shape = RoundedCornerShape(12.dp), border = androidx.compose.foundation.BorderStroke(1.dp, meterColor.copy(alpha = 0.3f)), modifier = modifier.fillMaxWidth() ) { Column(modifier = Modifier.padding(12.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Text( text = if (lang == AppLanguage.BN) "ঝুঁকি সূচক (Risk Index)" else "Risk Index Score", style = MaterialTheme.typography.labelMedium.copy( fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.onSurface ) ) Text( text = "${Localization.formatNumber(score, lang)} / ${Localization.formatNumber(100, lang)}", style = MaterialTheme.typography.titleMedium.copy( fontWeight = FontWeight.Black, color = meterColor ) ) } Spacer(modifier = Modifier.height(8.dp)) LinearProgressIndicator( progress = { score / 100f }, color = meterColor, trackColor = Color(0xFFE2E8F0), modifier = Modifier .fillMaxWidth() .height(8.dp) .clip(RoundedCornerShape(4.dp)) ) Spacer(modifier = Modifier.height(6.dp)) Text( text = if (lang == AppLanguage.BN) meterLabelBn else meterLabelEn, style = MaterialTheme.typography.bodySmall.copy( color = meterColor, fontWeight = FontWeight.Medium, fontSize = 12.sp ) ) } } } @Composable fun VisibleFieldLabel( label: String, isRequired: Boolean = false, modifier: Modifier = Modifier ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = modifier.padding(bottom = 6.dp) ) { Text( text = label, style = MaterialTheme.typography.labelLarge.copy( fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.onSurface ) ) if (isRequired) { Text( text = " *", style = MaterialTheme.typography.labelLarge.copy( fontWeight = FontWeight.Bold, color = StatusWarningRed ) ) } } } @Composable fun CustomLabeledTextField( label: String, value: String, onValueChange: (String) -> Unit, placeholder: String, isRequired: Boolean = false, errorMessage: String? = null, leadingIcon: ImageVector? = null, trailingIcon: @Composable (() -> Unit)? = null, singleLine: Boolean = true, maxLines: Int = 1, keyboardType: KeyboardType = KeyboardType.Text, testTag: String, modifier: Modifier = Modifier ) { Column(modifier = modifier.fillMaxWidth()) { VisibleFieldLabel(label = label, isRequired = isRequired) OutlinedTextField( value = value, onValueChange = onValueChange, placeholder = { Text( text = placeholder, style = MaterialTheme.typography.bodyMedium.copy(color = LightTextMuted) ) }, leadingIcon = if (leadingIcon != null) { { Icon( imageVector = leadingIcon, contentDescription = null, tint = if (errorMessage != null) StatusWarningRed else BrandNavyPrimary ) } } else null, trailingIcon = trailingIcon, singleLine = singleLine, maxLines = maxLines, isError = errorMessage != null, keyboardOptions = KeyboardOptions(keyboardType = keyboardType), shape = RoundedCornerShape(10.dp), colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = BrandBlueAccent, unfocusedBorderColor = LightOutline, errorBorderColor = StatusWarningRed, focusedContainerColor = MaterialTheme.colorScheme.surface, unfocusedContainerColor = MaterialTheme.colorScheme.surface ), modifier = Modifier .fillMaxWidth() .testTag(testTag) ) if (errorMessage != null) { Spacer(modifier = Modifier.height(4.dp)) Row(verticalAlignment = Alignment.CenterVertically) { Icon( imageVector = Icons.Default.ErrorOutline, contentDescription = "Error", tint = StatusWarningRed, modifier = Modifier.size(14.dp) ) Spacer(modifier = Modifier.width(4.dp)) Text( text = errorMessage, style = MaterialTheme.typography.bodySmall.copy( color = StatusWarningRed, fontSize = 12.sp, fontWeight = FontWeight.Medium ) ) } } } } @Composable fun StatusStepper( currentStatus: ReportStatus, lang: AppLanguage, modifier: Modifier = Modifier ) { val steps = listOf( Pair(ReportStatus.PENDING_REVIEW, if (lang == AppLanguage.BN) "অভিযোগ দাখিল" else "Submitted"), Pair(ReportStatus.UNDER_INVESTIGATION, if (lang == AppLanguage.BN) "প্রমাণ যাচাই" else "Verification"), Pair(ReportStatus.VERIFIED_WARNING_ISSUED, if (lang == AppLanguage.BN) "সতর্কতা জারি" else "Warning Issued"), Pair(ReportStatus.LEGAL_ESCALATION, if (lang == AppLanguage.BN) "আইনগত ব্যবস্থা" else "Legal Action"), Pair(ReportStatus.RESOLVED, if (lang == AppLanguage.BN) "নিষ্পন্ন" else "Resolved") ) val currentStepIndex = when (currentStatus) { ReportStatus.PENDING_REVIEW -> 0 ReportStatus.UNDER_INVESTIGATION -> 1 ReportStatus.VERIFIED_WARNING_ISSUED -> 2 ReportStatus.LEGAL_ESCALATION -> 3 ReportStatus.RESOLVED -> 4 ReportStatus.REJECTED -> 1 } Column( modifier = modifier .fillMaxWidth() .padding(vertical = 8.dp) ) { steps.forEachIndexed { index, (stepStatus, title) -> val isDone = index <= currentStepIndex && currentStatus != ReportStatus.REJECTED val isCurrent = index == currentStepIndex val isRejected = currentStatus == ReportStatus.REJECTED && index == 1 Row( verticalAlignment = Alignment.Top, modifier = Modifier.fillMaxWidth() ) { Column( horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.width(32.dp) ) { Box( modifier = Modifier .size(24.dp) .clip(CircleShape) .background( when { isRejected -> StatusWarningRed isDone -> BrandNavyPrimary else -> Color(0xFFE2E8F0) } ), contentAlignment = Alignment.Center ) { if (isDone) { Icon( imageVector = Icons.Default.Check, contentDescription = null, tint = Color.White, modifier = Modifier.size(14.dp) ) } else if (isRejected) { Icon( imageVector = Icons.Default.Close, contentDescription = null, tint = Color.White, modifier = Modifier.size(14.dp) ) } else { Text( text = "${index + 1}", style = MaterialTheme.typography.labelSmall.copy( color = LightTextMuted, fontSize = 11.sp ) ) } } if (index < steps.size - 1) { Box( modifier = Modifier .width(2.dp) .height(32.dp) .background(if (index < currentStepIndex && !isRejected) BrandNavyPrimary else Color(0xFFE2E8F0)) ) } } Spacer(modifier = Modifier.width(12.dp)) Column(modifier = Modifier.padding(bottom = 12.dp)) { Text( text = title, style = MaterialTheme.typography.bodyMedium.copy( fontWeight = if (isCurrent) FontWeight.Bold else FontWeight.Normal, color = if (isDone) MaterialTheme.colorScheme.onSurface else LightTextMuted ) ) if (isCurrent) { Text( text = if (isRejected) { if (lang == AppLanguage.BN) "পর্যাপ্ত প্রমাণের অভাবে অভিযোগটি বাতিল হয়েছে" else "Rejected due to insufficient proof" } else { if (lang == AppLanguage.BN) "বর্তমানে এই পর্যায়ে পর্যালোচনা চলছে" else "Currently undergoing official review" }, style = MaterialTheme.typography.bodySmall.copy( color = if (isRejected) StatusWarningRed else BrandBlueAccent, fontSize = 11.sp ) ) } } } } } }