package com.example.barokahstore.ui.screens import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.border import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Share import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.compose.ui.window.Dialog import com.example.barokahstore.data.DatabaseHelper import com.example.barokahstore.data.Settings import com.example.barokahstore.data.User @OptIn(ExperimentalMaterial3Api::class) @Composable fun PengaturanScreen( dbHelper: DatabaseHelper, currentUser: User ) { var selectedTab by remember { mutableStateOf(0) } // 0: Profil Toko, 1: Kelola Pengguna val tabs = listOf("Profil Toko", "Kelola Pengguna") Column( modifier = Modifier .fillMaxSize() .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { TabRow( selectedTabIndex = selectedTab, containerColor = Color.Transparent, contentColor = Color(0xFF4F46E5) ) { tabs.forEachIndexed { index, title -> Tab( selected = selectedTab == index, onClick = { selectedTab = index }, text = { Text(title, fontWeight = FontWeight.Bold, fontSize = 14.sp) } ) } } if (selectedTab == 0) { ProfilTokoTab(dbHelper = dbHelper) } else { KelolaPenggunaTab(dbHelper = dbHelper, currentUser = currentUser) } } } // --- PROFIL TOKO TAB --- @Composable fun ProfilTokoTab(dbHelper: DatabaseHelper) { val context = LocalContext.current var storeName by remember { mutableStateOf("") } var storeAddress by remember { mutableStateOf("") } var storePhone by remember { mutableStateOf("") } var receiptHeader by remember { mutableStateOf("") } var receiptFooter by remember { mutableStateOf("") } var thresholdText by remember { mutableStateOf("") } var alertMessage by remember { mutableStateOf("") } var isSuccess by remember { mutableStateOf(false) } // Backup & Restore state var backupRestoreMessage by remember { mutableStateOf("") } var backupRestoreSuccess by remember { mutableStateOf(false) } val backupLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument("application/octet-stream"), onResult = { uri -> if (uri != null) { val dbFile = context.getDatabasePath("barokah_store.db") try { context.contentResolver.openOutputStream(uri)?.use { outputStream -> java.io.FileInputStream(dbFile).use { inputStream -> inputStream.copyTo(outputStream) } } backupRestoreSuccess = true backupRestoreMessage = "Backup berhasil disimpan!" } catch (e: Exception) { e.printStackTrace() backupRestoreSuccess = false backupRestoreMessage = "Gagal backup: ${e.message}" } } } ) val restoreLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.GetContent(), onResult = { uri -> if (uri != null) { try { val dbFile = context.getDatabasePath("barokah_store.db") dbHelper.close() context.contentResolver.openInputStream(uri)?.use { inputStream -> java.io.FileOutputStream(dbFile).use { outputStream -> inputStream.copyTo(outputStream) } } // Reopen writable database connection dbHelper.writableDatabase backupRestoreSuccess = true backupRestoreMessage = "Restore berhasil! Silakan restart aplikasi." } catch (e: Exception) { e.printStackTrace() backupRestoreSuccess = false backupRestoreMessage = "Gagal restore: ${e.message}" } } } ) fun loadSettings() { val s = dbHelper.getSettings() storeName = s.storeName storeAddress = s.storeAddress storePhone = s.storePhone receiptHeader = s.receiptHeader receiptFooter = s.receiptFooter thresholdText = s.lowStockThreshold.toString() } LaunchedEffect(Unit) { loadSettings() } LazyColumn( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(16.dp) ) { item { Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color.White), elevation = CardDefaults.cardElevation(defaultElevation = 2.dp), modifier = Modifier.fillMaxWidth() ) { Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { Text("Pengaturan Toko & Struk", fontSize = 15.sp, fontWeight = FontWeight.Bold) if (alertMessage.isNotEmpty()) { Text( text = alertMessage, color = if (isSuccess) Color(0xFF059669) else Color.Red, fontSize = 13.sp, fontWeight = FontWeight.Medium ) } OutlinedTextField( value = storeName, onValueChange = { storeName = it }, label = { Text("Nama Toko") }, singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = storeAddress, onValueChange = { storeAddress = it }, label = { Text("Alamat Toko") }, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = storePhone, onValueChange = { storePhone = it }, label = { Text("Nomor Telepon Toko") }, singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = thresholdText, onValueChange = { thresholdText = it }, label = { Text("Batas Peringatan Stok Menipis") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = receiptHeader, onValueChange = { receiptHeader = it }, label = { Text("Header Struk (Header Receipt)") }, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = receiptFooter, onValueChange = { receiptFooter = it }, label = { Text("Footer Struk (Footer Receipt)") }, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) Button( onClick = { if (storeName.isBlank() || storeAddress.isBlank() || storePhone.isBlank() || thresholdText.isBlank()) { isSuccess = false alertMessage = "Kolom utama tidak boleh kosong!" return@Button } val threshold = thresholdText.toIntOrNull() ?: 10 val success = dbHelper.updateSettings( storeName = storeName.trim(), storeAddress = storeAddress.trim(), storePhone = storePhone.trim(), receiptHeader = receiptHeader.trim(), receiptFooter = receiptFooter.trim(), threshold = threshold ) if (success) { isSuccess = true alertMessage = "Pengaturan berhasil disimpan!" } else { isSuccess = false alertMessage = "Gagal menyimpan pengaturan!" } }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF4F46E5)), shape = RoundedCornerShape(10.dp), modifier = Modifier .fillMaxWidth() .height(45.dp) ) { Text("Simpan Pengaturan", fontWeight = FontWeight.Bold) } } } } // Backup & Restore Section item { Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color.White), elevation = CardDefaults.cardElevation(defaultElevation = 2.dp), modifier = Modifier.fillMaxWidth() ) { Column( modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { Text("Ekspor / Impor Database (Backup & Restore)", fontSize = 15.sp, fontWeight = FontWeight.Bold) Text( text = "Gunakan fitur ini untuk menyimpan cadangan database (.db) ke perangkat Anda atau memulihkannya kembali.", fontSize = 12.sp, color = Color.Gray ) if (backupRestoreMessage.isNotEmpty()) { Text( text = backupRestoreMessage, color = if (backupRestoreSuccess) Color(0xFF059669) else Color.Red, fontSize = 13.sp, fontWeight = FontWeight.Bold ) } Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { Button( onClick = { backupLauncher.launch("barokah_store_backup.db") }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF059669)), shape = RoundedCornerShape(10.dp), modifier = Modifier.weight(1f).height(45.dp) ) { Icon(Icons.Default.Share, contentDescription = null) Spacer(modifier = Modifier.width(4.dp)) Text("Backup", fontWeight = FontWeight.Bold) } Button( onClick = { restoreLauncher.launch("*/*") }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFD97706)), shape = RoundedCornerShape(10.dp), modifier = Modifier.weight(1f).height(45.dp) ) { Icon(Icons.Default.Refresh, contentDescription = null) Spacer(modifier = Modifier.width(4.dp)) Text("Restore", fontWeight = FontWeight.Bold) } } } } } } } // --- KELOLA PENGGUNA TAB --- @OptIn(ExperimentalMaterial3Api::class) @Composable fun KelolaPenggunaTab(dbHelper: DatabaseHelper, currentUser: User) { var usersList by remember { mutableStateOf(listOf()) } // Add / Edit user states var showDialog by remember { mutableStateOf(false) } var editingUser by remember { mutableStateOf(null) } // null = Add, else Edit var username by remember { mutableStateOf("") } var password by remember { mutableStateOf("") } var name by remember { mutableStateOf("") } var role by remember { mutableStateOf("kasir") } // 'admin' or 'kasir' var alertMessage by remember { mutableStateOf("") } var showDeleteDialog by remember { mutableStateOf(false) } var userToDelete by remember { mutableStateOf(null) } fun refreshUsers() { usersList = dbHelper.getAllUsers() } LaunchedEffect(Unit) { refreshUsers() } Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(12.dp) ) { Button( onClick = { editingUser = null username = "" password = "" name = "" role = "kasir" alertMessage = "" showDialog = true }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF4F46E5)), shape = RoundedCornerShape(10.dp), modifier = Modifier .align(Alignment.End) .height(45.dp) ) { Icon(Icons.Default.Add, contentDescription = null) Spacer(modifier = Modifier.width(4.dp)) Text("Pengguna") } Card( modifier = Modifier .fillMaxWidth() .weight(1f), shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color.White), elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) ) { LazyColumn(modifier = Modifier.padding(16.dp)) { items(usersList) { user -> Row( modifier = Modifier .fillMaxWidth() .padding(vertical = 6.dp) .border(0.5.dp, Color(0xFFE5E7EB), RoundedCornerShape(10.dp)) .padding(12.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column { Text(user.name, fontWeight = FontWeight.Bold, fontSize = 14.sp) Text("Username: ${user.username} • Role: ${user.role.uppercase()}", fontSize = 11.sp, color = Color.Gray) } val canModify = user.id != currentUser.id if (canModify) { Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { IconButton( onClick = { editingUser = user username = user.username password = "" // Leave blank to not change password name = user.name role = user.role alertMessage = "" showDialog = true }, modifier = Modifier.size(24.dp) ) { Icon(Icons.Default.Edit, contentDescription = null, tint = Color(0xFF3B82F6)) } IconButton( onClick = { userToDelete = user showDeleteDialog = true }, modifier = Modifier.size(24.dp) ) { Icon(Icons.Default.Delete, contentDescription = null, tint = Color(0xFFEF4444)) } } } } } } } } // --- ADD / EDIT USER DIALOG --- if (showDialog) { Dialog(onDismissRequest = { showDialog = false }) { Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color.White), modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { LazyColumn( modifier = Modifier.padding(24.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { item { Text( text = if (editingUser == null) "Tambah Pengguna" else "Edit Pengguna", fontSize = 18.sp, fontWeight = FontWeight.Bold, color = Color(0xFF1F2937), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center ) } if (alertMessage.isNotEmpty()) { item { Text(alertMessage, color = Color.Red, fontSize = 13.sp) } } item { OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Nama Lengkap") }, singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = username, onValueChange = { username = it }, label = { Text("Username") }, singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = password, onValueChange = { password = it }, label = { Text(if (editingUser == null) "Password" else "Password Baru (Kosongkan jika tidak diubah)") }, singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) } item { Text("Pilih Role:", fontSize = 12.sp, color = Color.Gray) Row( modifier = Modifier.fillMaxWidth().padding(top = 4.dp), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { Button( onClick = { role = "kasir" }, colors = ButtonDefaults.buttonColors( containerColor = if (role == "kasir") Color(0xFF4F46E5) else Color(0xFFE5E7EB) ), shape = RoundedCornerShape(8.dp), modifier = Modifier.weight(1f) ) { Text("Kasir", color = if (role == "kasir") Color.White else Color.Black) } Button( onClick = { role = "admin" }, colors = ButtonDefaults.buttonColors( containerColor = if (role == "admin") Color(0xFF4F46E5) else Color(0xFFE5E7EB) ), shape = RoundedCornerShape(8.dp), modifier = Modifier.weight(1f) ) { Text("Admin", color = if (role == "admin") Color.White else Color.Black) } } } item { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { OutlinedButton( onClick = { showDialog = false }, modifier = Modifier.weight(1f) ) { Text("Batal") } Button( onClick = { if (name.isBlank() || username.isBlank() || (editingUser == null && password.isBlank())) { alertMessage = "Kolom utama tidak boleh kosong!" return@Button } val success: Boolean if (editingUser == null) { val existing = usersList.find { it.username == username.trim() } if (existing != null) { alertMessage = "Username sudah digunakan!" return@Button } success = dbHelper.insertUser(username.trim(), password, name.trim(), role) } else { success = dbHelper.updateUser( id = editingUser!!.id, username = username.trim(), name = name.trim(), role = role, passwordText = password.trim().ifEmpty { null } ) } if (success) { refreshUsers() showDialog = false } else { alertMessage = "Gagal menyimpan akun pengguna!" } }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF4F46E5)), modifier = Modifier.weight(1f) ) { Text("Simpan") } } } } } } } // --- DELETE USER CONFIRM --- if (showDeleteDialog && userToDelete != null) { AlertDialog( onDismissRequest = { showDeleteDialog = false }, title = { Text("Hapus Akun Pengguna") }, text = { Text("Apakah Anda yakin ingin menghapus akun '${userToDelete!!.name}'?") }, confirmButton = { Button( onClick = { dbHelper.deleteUser(userToDelete!!.id) refreshUsers() showDeleteDialog = false }, colors = ButtonDefaults.buttonColors(containerColor = Color.Red) ) { Text("Hapus", color = Color.White) } }, dismissButton = { TextButton(onClick = { showDeleteDialog = false }) { Text("Batal") } } ) } }