From b7c04719c7e11f88cbb4726d193f0508793aaf28 Mon Sep 17 00:00:00 2001 From: moon <56061215+MgKyawLay@users.noreply.github.com> Date: Wed, 10 Jun 2026 20:17:33 +0630 Subject: [PATCH] side bar modified --- .../ui/dashboard/DashboardScreen2.kt | 638 ++++++++++++++++++ 1 file changed, 638 insertions(+) create mode 100644 app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardScreen2.kt diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardScreen2.kt b/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardScreen2.kt new file mode 100644 index 0000000..b250bc4 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardScreen2.kt @@ -0,0 +1,638 @@ +package com.mob.utsmyanmar.ui.dashboard + +import android.os.Handler +import android.os.Looper +import androidx.compose.animation.core.tween +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import coil3.compose.AsyncImage +import com.mob.utsmyanmar.R +import com.mob.utsmyanmar.ui.components.appbar.AppBar +import com.mob.utsmyanmar.ui.device_info.DeviceInfoViewModel +import com.mob.utsmyanmar.ui.preview.P2Preview +import com.mob.utsmyanmar.ui.preview.P3Preview +import com.mob.utsmyanmar.ui.theme.Color +import com.utsmyanmar.paylibs.sign_on.EchoTestProcess +import com.utsmyanmar.paylibs.sign_on.SignOnListener +import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +@Composable +fun DashboardScreen2( + onNavigateAmount: (String) -> Unit = {}, + onNavigateSignOn: () -> Unit = {}, + onNavigateSeeMore: () -> Unit = {}, + onNavigateSettlement: () -> Unit = {}, + onNavigateVersion: () -> Unit = {}, + onNavigateFunctions: () -> Unit = {}, + deviceInfoViewModel: DeviceInfoViewModel = viewModel() +) { + val deviceInfo by deviceInfoViewModel.uiState.collectAsState() + + LaunchedEffect(Unit) { + deviceInfoViewModel.loadDeviceInfo() + } + + val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) + val scope = rememberCoroutineScope() + val mainHandler = remember { Handler(Looper.getMainLooper()) } + var showHostActionDialog by remember { mutableStateOf(false) } + var activeHostAction by remember { mutableStateOf("Log-On") } + var isHostActionRunning by remember { mutableStateOf(false) } + var dialogMessage by remember { mutableStateOf("") } + + val isOnline = true + + fun confirmationMessage(action: String) = "Do you want to start ${action.lowercase()}?" + fun processingMessage(action: String) = "Sending ${action.lowercase()} request to host..." + fun successMessage(action: String) = "$action success." + fun failureMessage(action: String, resultCode: Int?) = + "$action failed. Response code: ${resultCode ?: -1}" + + fun networkFailureMessage(action: String) = "Network error during $action." + fun openHostActionDialog(action: String) { + activeHostAction = action + isHostActionRunning = false + dialogMessage = confirmationMessage(action) + showHostActionDialog = true + } + + if (showHostActionDialog) { + AlertDialog( + onDismissRequest = { + if (!isHostActionRunning) { + showHostActionDialog = false + } + }, title = { + Text( + text = activeHostAction, color = Color.LegacyRed, fontWeight = FontWeight.Bold + ) + }, text = { + Text(text = dialogMessage) + }, confirmButton = { + TextButton( + onClick = { + if (isHostActionRunning) return@TextButton + + if (dialogMessage != confirmationMessage(activeHostAction)) { + showHostActionDialog = false + dialogMessage = confirmationMessage(activeHostAction) + return@TextButton + } + + isHostActionRunning = true + dialogMessage = processingMessage(activeHostAction) + + val signOnProcess = EchoTestProcess.getInstance() + val request = when (activeHostAction) { + "Echo Test" -> signOnProcess.enqueue(false) + "Log-Off" -> signOnProcess.enqueueLogOff() + else -> signOnProcess.enqueueLogOn() + } + + request.startSignOn(object : SignOnListener { + override fun onSuccessSignOn() { + mainHandler.post { + isHostActionRunning = false + dialogMessage = successMessage(activeHostAction) + } + } + + override fun onFailureSignOn(resultCode: Int?) { + mainHandler.post { + isHostActionRunning = false + mainHandler.post { + isHostActionRunning = false + dialogMessage = failureMessage(activeHostAction, resultCode) + } + } + } + + + override fun onNetworkFailSignOn(message: String?) { + mainHandler.post { + isHostActionRunning = false + dialogMessage = message?.takeIf { it.isNotBlank() } + ?: networkFailureMessage(activeHostAction) + } + } + }) + }) { + Text( + text = when { + isHostActionRunning -> "Processing" + dialogMessage == confirmationMessage(activeHostAction) -> "Start" + else -> "Close" + }, color = Color.LegacyRed + ) + } + }, dismissButton = { + if (!isHostActionRunning) { + TextButton( + onClick = { + showHostActionDialog = false + dialogMessage = confirmationMessage(activeHostAction) + }) { + Text(text = "Cancel", color = Color.Gray) + } + } + }, containerColor = Color.White + ) + } + + ModalNavigationDrawer( + drawerState = drawerState, drawerContent = { + ModalDrawerSheet( + modifier = Modifier.fillMaxWidth(0.78f), + drawerContainerColor = Color.White, + drawerShape = RoundedCornerShape(topEnd = 24.dp, bottomEnd = 24.dp) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.IvoryBeige) + .padding(horizontal = 20.dp, vertical = 28.dp) + ) { + Row { + Box( + modifier = Modifier + .size(80.dp) + .clip(CircleShape) + .background(Color.White), contentAlignment = Alignment.Center + ) { + Image( + painter = painterResource(R.drawable.logo_mob), + contentDescription = "mob logo", + modifier = Modifier + .size(100.dp) + .padding(16.dp), + contentScale = ContentScale.Fit + ) + } + + Column { + Text( + text = "MOB Merchant", + color = Color.CrimsonRed, + fontSize = 12.sp, + fontWeight = FontWeight.Bold + ) + Text( + text = "S/N:${deviceInfo.serialNumber}", + color = Color.Black, + fontSize = 12.sp, + fontWeight = FontWeight.Bold + ) + Text( + text = if (isOnline) "Online" else "Offline", + color = if (isOnline) Color.Success else Color.Error, + fontSize = 12.sp, + fontWeight = FontWeight.Bold + ) + } + } + } + + Spacer(modifier = Modifier.height(10.dp)) + + DrawerItem("Log-On", Icons.Default.Dashboard) { + scope.launch { drawerState.close() } + openHostActionDialog("Log-On") + } + DrawerItem("Echo Test", Icons.Default.Sync) { + scope.launch { drawerState.close() } + openHostActionDialog("Echo Test") + } + DrawerItem("Log-Off", Icons.Default.Dashboard) { + scope.launch { drawerState.close() } + openHostActionDialog("Log-Off") + } + Text( + text = "System Management", + fontWeight = FontWeight.Medium, + modifier = Modifier.padding(horizontal = 16.dp) + ) + + var switchChecked by remember { mutableStateOf(SystemParamsOperation.getInstance().isReversalOn) } + DrawerItem( + title = "Reversal On/Off", + icon = Icons.Default.Sync, + showSwitch = true, + isChecked = switchChecked, + onCheckedChange = { isChecked -> + switchChecked = isChecked + SystemParamsOperation.getInstance().setReversalFlag(isChecked) + + }, + onClick = {} + ) + DrawerItem("Function", Icons.Default.Dashboard) { + scope.launch { drawerState.close() } + onNavigateFunctions() + } + DrawerItem("Version", Icons.Default.Dashboard) { + scope.launch { drawerState.close() } + onNavigateVersion() + } + } + }) { + Scaffold( + containerColor = Color.IvoryBeige, topBar = { + AppBar( + title = "Dashboard", + icon = Icons.Default.Menu, + onIconClick = { scope.launch { drawerState.open() } }) + }) { paddingValues -> + Column( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize() + ) { + //top section + Box( + modifier = Modifier + .weight(1f) + .fillMaxWidth(), + ) { + AdvertisingArea() + } + //center section + Box( + modifier = Modifier + .weight(1f) + .fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + SummaryCard() + } + //bottom section + Box( + modifier = Modifier + .weight(1.5f) + .fillMaxWidth(), + ) { + MenuGrid( + onNavigateAmount = onNavigateAmount, + onNavigateSignOn = onNavigateSignOn, + onNavigateSeeMore = onNavigateSeeMore, + onNavigateSettlement = onNavigateSettlement + ) + } + } + } + } +} + +@Composable +private fun DrawerItem( + title: String, + icon: ImageVector, + showSwitch: Boolean = false, // New: Flag to enable switch mode + isChecked: Boolean = false, // New: Switch state + onCheckedChange: (Boolean) -> Unit = {}, // New: Switch callback + onClick: () -> Unit +) { + NavigationDrawerItem( + label = { + Text( + text = title, + fontWeight = FontWeight.Medium + ) + }, + selected = false, + // If it's a switch item, clicking the whole row toggles the switch instead of navigating + onClick = { + if (showSwitch) { + onCheckedChange(!isChecked) + } else { + onClick() + } + }, + icon = { + Icon( + imageVector = icon, + contentDescription = title + ) + }, + + badge = { + if (showSwitch) { + Switch( + checked = isChecked, + onCheckedChange = onCheckedChange + ) + } + }, + modifier = Modifier.padding(horizontal = 12.dp, vertical = 2.dp), + colors = NavigationDrawerItemDefaults.colors( + unselectedContainerColor = androidx.compose.ui.graphics.Color.Transparent, + unselectedIconColor = Color.LegacyRed, + unselectedTextColor = Color.Black + ) + ) +} + +@Composable +private fun AdvertisingArea() { + + val imageArray = listOf( + "https://i.ytimg.com/vi/eRUVxGRp1Ms/maxresdefault.jpg", + "https://i.ytimg.com/vi/AwvmgTPd7qw/maxresdefault.jpg", + "https://mma.prnewswire.com/media/2080956/SUNMI_3rd_generation_products_T3_PRO_series_V3_MIX.jpg?p=facebook" + ) + val pageState = rememberPagerState(pageCount = { imageArray.size }) + + LaunchedEffect(pageState) { + while (true) { + delay(10000) + val nextPage = (pageState.currentPage + 1) % imageArray.size + + pageState.animateScrollToPage( + page = nextPage, animationSpec = tween(durationMillis = 700) + ) + } + } + + Card( + modifier = Modifier.fillMaxSize(), + shape = RoundedCornerShape(0.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + ) { + HorizontalPager( + state = pageState, modifier = Modifier.fillMaxSize() + ) { page -> + AsyncImage( + model = imageArray[page], + contentDescription = "ads images", + modifier = Modifier.fillMaxSize(), + contentScale = ContentScale.Crop + ) + } + } +} + +@Composable +private fun SummaryCard() { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + elevation = CardDefaults.cardElevation(4.dp) + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Column(modifier = Modifier.weight(1f)) { + SummaryItem( + title = "Sales Today", + value = "MMK 2,000,000", + subtitle = "47 Transactions", + icon = Icons.Default.BarChart, + iconBg = Color.CrimsonRed + ) + + HorizontalDivider( + color = Color.Gray, + modifier = Modifier.padding(vertical = 6.dp, horizontal = 6.dp) + ) + + SummaryItem( + title = "Settlement", + value = "Completed", + subtitle = "Today 11:00 PM", + icon = Icons.Default.Check, + iconBg = Color.CrimsonRed + ) + } + + VerticalDivider(color = Color.Gray, modifier = Modifier.height(160.dp)) + + Column( + modifier = Modifier + .weight(0.75f) + .padding(start = 18.dp), + horizontalAlignment = Alignment.Start + ) { + Row(verticalAlignment = Alignment.Bottom) { + Text( + text = "Last Sync", fontSize = 12.sp + ) + IconCircle(Icons.Default.Sync, Color.CrimsonRed) + } + + Spacer(Modifier.height(6.dp)) + + Text( + text = "5 mins ago", + fontSize = 18.sp, + color = Color.LegacyRed, + fontWeight = FontWeight.SemiBold + ) + } + } + } +} + +@Composable +private fun SummaryItem( + title: String, + value: String, + subtitle: String, + icon: ImageVector, + iconBg: androidx.compose.ui.graphics.Color +) { + Row( + verticalAlignment = Alignment.Top, modifier = Modifier.padding(6.dp) + ) { + Column(modifier = Modifier.weight(1f)) { + Text(title, fontSize = 12.sp) + Text( + value, fontSize = 14.sp, color = Color.LegacyRed, fontWeight = FontWeight.Bold + ) + Text(subtitle, fontSize = 12.sp) + } + IconCircle(icon, iconBg) + } +} + +@Composable +private fun IconCircle( + icon: ImageVector, color: androidx.compose.ui.graphics.Color +) { + Box( + modifier = Modifier + .size(38.dp) + .clip(CircleShape) + .background(color.copy(alpha = 0.1f)), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = icon, + contentDescription = null, + tint = color, + modifier = Modifier.size(22.dp) + ) + } +} + +@Composable +private fun MenuGrid( + onNavigateAmount: (String) -> Unit, + onNavigateSignOn: () -> Unit, + onNavigateSeeMore: () -> Unit, + onNavigateSettlement: () -> Unit +) { + Column( + verticalArrangement = Arrangement.spacedBy(10.dp), + modifier = Modifier.padding(horizontal = 16.dp) + ) { + Spacer(Modifier.height(8.dp)) + Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) { + MenuCard(title = "Sale", icon = { + Icon( + painterResource(R.drawable.ic_terminal), + contentDescription = "icon", + modifier = Modifier.size(40.dp), + tint = Color.LegacyRed + ) + }, modifier = Modifier.weight(1f), onClick = { onNavigateAmount("Sale") }) + MenuCard(title = "MMQR", icon = { + Image( + painter = painterResource(R.drawable.ic_mmqr_logo), + contentDescription = "mmqr image", + modifier = Modifier.height(48.dp) + ) + }, modifier = Modifier.weight(1f)) + MenuCard("History", icon = { + Icon( + painterResource(R.drawable.ic_history), + contentDescription = "icon", + modifier = Modifier.size(32.dp), + tint = Color.LegacyRed + ) + }, modifier = Modifier.weight(1f)) + } + + Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) { + MenuCard( + title = "Sign On", icon = { + Icon( + painterResource(R.drawable.ic_sign_on), + contentDescription = "icon", + modifier = Modifier.size(32.dp), + tint = Color.LegacyRed + ) + }, modifier = Modifier.weight(1f), onClick = onNavigateSignOn + ) + MenuCard( + title = "Settlement", icon = { + Icon( + painterResource(R.drawable.ic_settlement), + contentDescription = "icon", + modifier = Modifier.size(32.dp), + tint = Color.LegacyRed + ) + }, modifier = Modifier.weight(1f), onClick = onNavigateSettlement + ) + MenuCard( + title = "See More", icon = { + Icon( + painterResource(R.drawable.ic_see_more), + contentDescription = "icon", + modifier = Modifier.size(32.dp), + tint = Color.LegacyRed + ) + }, modifier = Modifier.weight(1f), onClick = onNavigateSeeMore + ) + } + } +} + +@Composable +private fun MenuCard( + title: String, + icon: @Composable () -> Unit, + modifier: Modifier = Modifier, + onClick: (() -> Unit)? = null +) { + Card( + modifier = modifier + .height(100.dp) + .then( + if (onClick != null) { + Modifier.clickable(onClick = onClick) + } else { + Modifier + } + ), + shape = RoundedCornerShape(14.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + elevation = CardDefaults.cardElevation(5.dp) + ) { + Box( + modifier = Modifier + .fillMaxSize() + .padding(6.dp) + ) { + Column { + icon() + Spacer(modifier = Modifier.weight(1f)) + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = title, + fontSize = 12.sp, + color = Color.Black, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + modifier = Modifier.weight(1f) + ) + Icon( + imageVector = Icons.Default.ChevronRight, + contentDescription = null, + tint = Color.LegacyRed, + modifier = Modifier.size(28.dp) + ) + } + } + } + } +} + +@P2Preview +@Composable +fun PreviewDashboardScreen2() { + DashboardScreen2() +} + +@P3Preview +@Composable +fun PreviewDashboardScreen3() { + DashboardScreen2() +}