diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardComponents.kt b/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardComponents.kt index 936ee76..fcba6ed 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardComponents.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardComponents.kt @@ -362,7 +362,10 @@ private fun MenuCard( } } -fun LazyListScope.recentTransactionsItems(transactions: List) { +fun LazyListScope.recentTransactionsItems( + transactions: List, + onTransactionClick: (TrnxRecord) -> Unit = {} +) { item { Row( modifier = Modifier @@ -394,19 +397,32 @@ fun LazyListScope.recentTransactionsItems(transactions: List) { } } else { items(items = transactions, key = { it.pid }) { record -> - TrnxRow(record = record) + TrnxRow( + record = record, + onClick = { onTransactionClick(record) } + ) } } } @Composable -private fun TrnxRow(record: TrnxRecord) { +private fun TrnxRow( + record: TrnxRecord, + onClick: (() -> Unit)? = null +) { Row( modifier = Modifier .fillMaxWidth() .padding(horizontal = 12.dp, vertical = 4.dp) .clip(RoundedCornerShape(10.dp)) .background(Color.White) + .then( + if (onClick != null) { + Modifier.clickable(onClick = onClick) + } else { + Modifier + } + ) .padding(horizontal = 12.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically ) { @@ -427,7 +443,7 @@ private fun TrnxRow(record: TrnxRecord) { Column(modifier = Modifier.weight(1f)) { Text( - text =record.typeLabel, + text = record.typeLabel, style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.SemiBold), color = Color.Black ) diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardScreen.kt b/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardScreen.kt index db63524..f3b9f9c 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardScreen.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/dashboard/DashboardScreen.kt @@ -72,6 +72,7 @@ fun DashboardScreen2( dashboardUiState: DashboardUiState = DashboardUiState(), deviceInfo: DeviceInfoUiState = DeviceInfoUiState(), onClickLastSync: () -> Unit = {}, + onNavigateRecentTransaction: (TrnxRecord) -> Unit = {}, ) { val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) val scope = rememberCoroutineScope() @@ -410,7 +411,10 @@ fun DashboardScreen2( ) } item { Spacer(Modifier.height(8.dp)) } - recentTransactionsItems(transactions = dashboardUiState.recentTransactions) + recentTransactionsItems( + transactions = dashboardUiState.recentTransactions, + onTransactionClick = onNavigateRecentTransaction + ) } } } diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt index 87bd956..4a12a7f 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt @@ -6,6 +6,8 @@ import androidx.activity.ComponentActivity import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.platform.LocalContext import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -15,7 +17,6 @@ import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.navArgument -import com.mob.utsmyanmar.model.ProcessCode import com.mob.utsmyanmar.ui.cardwaiting.CardWaitingScreen import com.mob.utsmyanmar.ui.cardwaiting.CardWaitingViewModel import com.mob.utsmyanmar.ui.dashboard.DashboardScreen2 @@ -42,6 +43,7 @@ import com.mob.utsmyanmar.ui.settlement.SettlementScreen import com.mob.utsmyanmar.ui.settlement.SettlementViewModel import com.mob.utsmyanmar.ui.sign_on.SignOnResultScreen import com.mob.utsmyanmar.ui.sign_on.SignOnRoute +import com.mob.utsmyanmar.ui.signature_pad.SignaturePad import com.mob.utsmyanmar.ui.tms_setup.TmsSetupRoute import com.mob.utsmyanmar.ui.tms_setup.TmsSetupViewModel import com.mob.utsmyanmar.ui.transaction_result.TransactionResultEvent @@ -83,11 +85,20 @@ fun AppNavGraph( ) } + composable(Routes.Signature.route) { + SignaturePad( + onBack = { navController.popBackStack() }, + onConfirm = { navController.popBackStack() } + ) + } + composable(Routes.Dashboard.route) { val sharedViewModel: SharedViewModel = hiltViewModel(activity) val transProcessViewModel: TransProcessViewModel = hiltViewModel(activity) + val transResultViewModel: TransactionResultViewModel = hiltViewModel(activity) val dashboardViewModel: DashboardViewModel = hiltViewModel(activity) val dashboardUiState = dashboardViewModel.uiState.collectAsStateWithLifecycle().value + val recentPayDetails by sharedViewModel.getLastThreeTransactions().observeAsState(emptyList()) val deviceInfoViewModel: DeviceInfoViewModel = hiltViewModel(activity) val deviceInfoUiState = deviceInfoViewModel.uiState.collectAsStateWithLifecycle().value val tmsSetupViewModel: TmsSetupViewModel = hiltViewModel(); @@ -109,6 +120,18 @@ fun AppNavGraph( onClickLastSync = { tmsSetupViewModel.downloadConfigs() }, + onNavigateRecentTransaction = { record -> + recentPayDetails.firstOrNull { it.PID == record.pid }?.let { payDetail -> + sharedViewModel.payDetail.value = payDetail + transResultViewModel.onEvent( + TransactionResultEvent.Start, + sharedViewModel + ) + navController.navigate(Routes.PrintReceipt.route) { + launchSingleTop = true + } + } + }, onNavigateAmount = { action -> when (action) { "Pre-Auth", diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt index fc05680..6ba9067 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/Routes.kt @@ -3,6 +3,7 @@ package com.mob.utsmyanmar.ui.navigation import android.net.Uri sealed class Routes(val route: String) { + data object Signature: Routes("signature") data object TmsSetup : Routes("tms_setup") data object Dashboard : Routes("dashboard") data object Amount : Routes("amount/{action}") { diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/signature_pad/SignaturePad.kt b/app/src/main/java/com/mob/utsmyanmar/ui/signature_pad/SignaturePad.kt new file mode 100644 index 0000000..66dedaa --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/ui/signature_pad/SignaturePad.kt @@ -0,0 +1,231 @@ +package com.mob.utsmyanmar.ui.signature_pad + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.mob.utsmyanmar.ui.components.appbar.AppBar +import com.mob.utsmyanmar.ui.theme.MOBPOSTheme +import com.mob.utsmyanmar.ui.theme.Color as AppColor + +@Composable +fun SignaturePad( + modifier: Modifier = Modifier, + signatureColor: Color = Color.Black, + strokeWidth: Float = 8f, + onBack: () -> Unit = {}, + onConfirm: () -> Unit = {} +) { + val paths = remember { mutableStateListOf>() } + val currentPath = remember { mutableStateListOf() } + var selectedMode by remember { mutableStateOf(SignatureMode.Manual) } + fun clearSignature() { + paths.clear() + currentPath.clear() + } + + Scaffold( + topBar = { + AppBar( + title = "Signature", + onIconClick = onBack + ) + }, + containerColor = AppColor.IvoryBeige + ) { paddingValues -> + Column( + modifier = modifier + .padding(paddingValues) + .fillMaxSize() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Box( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + .background(Color.White, RoundedCornerShape(16.dp)) + .padding(12.dp) + .pointerInput(Unit) { + detectDragGestures( + onDragStart = { startOffset -> + currentPath.add(startOffset) + }, + onDrag = { change, _ -> + change.consume() + currentPath.add(change.position) + }, + onDragEnd = { + paths.add(currentPath.toList()) + currentPath.clear() + } + ) + } + ) { + Canvas(modifier = Modifier.fillMaxSize()) { + paths.forEach { points -> + if (points.size > 1) { + val path = Path().apply { + moveTo(points.first().x, points.first().y) + for (i in 1 until points.size) { + lineTo(points[i].x, points[i].y) + } + } + drawPath( + path = path, + color = signatureColor, + style = Stroke(width = strokeWidth) + ) + } + } + + if (currentPath.size > 1) { + val path = Path().apply { + moveTo(currentPath.first().x, currentPath.first().y) + for (i in 1 until currentPath.size) { + lineTo(currentPath[i].x, currentPath[i].y) + } + } + drawPath( + path = path, + color = signatureColor, + style = Stroke(width = strokeWidth) + ) + } + } + } + + Column ( + modifier = Modifier.fillMaxWidth().height(150.dp), +// horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + SignatureModeOption( + title = "Manual Sign", + subtitle = "Customer signs on screen", + selected = selectedMode == SignatureMode.Manual, + onClick = { selectedMode = SignatureMode.Manual }, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(Modifier.height(12.dp)) + + SignatureModeOption( + title = "Digital Sign", + subtitle = "Use stored signature", + selected = selectedMode == SignatureMode.Digital, + onClick = { selectedMode = SignatureMode.Digital }, + modifier = Modifier.fillMaxWidth() + ) + } + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + OutlinedButton( + onClick = { clearSignature() }, + modifier = Modifier + .weight(1f) + .height(50.dp) + ) { + Text("Clear") + } + + Button( + onClick = onConfirm, + modifier = Modifier + .weight(1f) + .height(50.dp), + colors = ButtonDefaults.buttonColors( + containerColor = AppColor.LegacyRed, + contentColor = Color.White + ) + ) { + Text("Confirm") + } + } + } + } +} + +private enum class SignatureMode { + Manual, + Digital +} + +@Composable +private fun SignatureModeOption( + title: String, + subtitle: String, + selected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Card( + onClick = onClick, + modifier = modifier, + shape = RoundedCornerShape(14.dp), + colors = CardDefaults.cardColors( + containerColor = if (selected) AppColor.LegacyRed.copy(alpha = 0.10f) else Color.White + ), + border = androidx.compose.foundation.BorderStroke( + width = 1.dp, + color = if (selected) AppColor.LegacyRed else AppColor.Gray.copy(alpha = 0.35f) + ) + ) { + Column( + modifier = Modifier.padding(14.dp), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = title, + style = MaterialTheme.typography.titleSmall, + color = if (selected) AppColor.LegacyRed else Color.Black + ) + Text( + text = subtitle, + style = MaterialTheme.typography.bodySmall, + color = AppColor.Gray + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun SignaturePadPreview() { + MOBPOSTheme { + SignaturePad() + } +}