package com.example.barokahstore.ui.screens import android.app.DatePickerDialog 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.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.filled.* 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.FontFamily 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.Receivable import com.example.barokahstore.data.ReceivablePayment import com.example.barokahstore.data.User import java.util.Calendar @OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class) @Composable fun PiutangScreen( dbHelper: DatabaseHelper, currentUser: User ) { var selectedFilter by remember { mutableStateOf("all") } // 'all', 'unpaid', 'partial', 'paid' var receivablesList by remember { mutableStateOf(listOf()) } var searchQuery by remember { mutableStateOf("") } // Dialog installment states var showPayDialog by remember { mutableStateOf(false) } var selectedReceivable by remember { mutableStateOf(null) } var payAmountText by remember { mutableStateOf("") } var noteText by remember { mutableStateOf("") } var alertMessage by remember { mutableStateOf("") } // Dialog manual receivable states var showAddManualDialog by remember { mutableStateOf(false) } var manualCustomerName by remember { mutableStateOf("") } var manualTotalAmountText by remember { mutableStateOf("") } var manualNotesText by remember { mutableStateOf("") } var manualAlertMessage by remember { mutableStateOf("") } // Expanded items for showing history logs val expandedItems = remember { mutableStateMapOf() } val context = LocalContext.current val calendar = Calendar.getInstance() fun refreshReceivables() { val filter = if (selectedFilter == "all") null else selectedFilter var list = dbHelper.getAllReceivables(filter) if (searchQuery.isNotEmpty()) { list = list.filter { it.customerName.contains(searchQuery, ignoreCase = true) || it.invoiceNo.contains(searchQuery) } } receivablesList = list } LaunchedEffect(selectedFilter, searchQuery) { refreshReceivables() } Column( modifier = Modifier .fillMaxSize() .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { // Search & Add Button Row Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp), verticalAlignment = Alignment.CenterVertically ) { OutlinedTextField( value = searchQuery, onValueChange = { searchQuery = it }, placeholder = { Text("Cari nama pelanggan / invoice...") }, leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) }, singleLine = true, shape = RoundedCornerShape(12.dp), modifier = Modifier.weight(1f) ) Button( onClick = { manualCustomerName = "" manualTotalAmountText = "" manualNotesText = "" manualAlertMessage = "" showAddManualDialog = true }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF4F46E5)), shape = RoundedCornerShape(12.dp), modifier = Modifier.height(50.dp) ) { Icon(Icons.Default.Add, contentDescription = null) Spacer(modifier = Modifier.width(4.dp)) Text("Manual") } } // Filter chips row FlowRow( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp) ) { val filters = listOf( Pair("Semua", "all"), Pair("Belum Lunas", "unpaid"), Pair("Dicicil", "partial"), Pair("Lunas", "paid") ) filters.forEach { (label, key) -> val isSelected = selectedFilter == key FilterChip( selected = isSelected, onClick = { selectedFilter = key }, label = { Text(label) } ) } } // List Card( modifier = Modifier .fillMaxWidth() .weight(1f), shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color.White), elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) ) { if (receivablesList.isEmpty()) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Text("Data piutang tidak ditemukan", color = Color.Gray, fontSize = 14.sp) } } else { LazyColumn(modifier = Modifier.padding(16.dp)) { items(receivablesList) { rec -> val isExpanded = expandedItems[rec.id] ?: false Column( modifier = Modifier .fillMaxWidth() .padding(vertical = 8.dp) .border(0.5.dp, Color(0xFFE5E7EB), RoundedCornerShape(10.dp)) .padding(12.dp) ) { // Summary Row Row( modifier = Modifier .fillMaxWidth() .clickable { expandedItems[rec.id] = !isExpanded }, horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column(modifier = Modifier.weight(1f)) { Text(rec.customerName, fontWeight = FontWeight.Bold, fontSize = 14.sp) val dueText = if (rec.dueDate.isNullOrEmpty()) "Manual" else rec.dueDate Text("Invoice: ${rec.invoiceNo} • Jatuh Tempo: $dueText", fontSize = 11.sp, color = Color.Gray) if (!rec.notes.isNullOrEmpty()) { Text("Keterangan: ${rec.notes}", fontSize = 11.sp, color = Color.DarkGray, fontWeight = FontWeight.Medium) } val remaining = rec.totalAmount - rec.paidAmount Row(modifier = Modifier.padding(top = 4.dp), horizontalArrangement = Arrangement.spacedBy(12.dp)) { Text("Total: ${formatRupiah(rec.totalAmount)}", fontSize = 11.sp, color = Color.Gray) Text("Sisa: ${formatRupiah(remaining)}", fontSize = 12.sp, fontWeight = FontWeight.Bold, color = Color.Red) } if (!rec.paidBy.isNullOrEmpty()) { Text("Diterima oleh: ${rec.paidBy}", fontSize = 11.sp, color = Color(0xFF059669), fontWeight = FontWeight.Medium, modifier = Modifier.padding(top = 4.dp)) } } Column(horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.spacedBy(8.dp)) { Surface( color = when(rec.status) { "paid" -> Color(0xFFD1FAE5) "partial" -> Color(0xFFFEF3C7) else -> Color(0xFFFEE2E2) }, shape = RoundedCornerShape(6.dp) ) { Text( text = when(rec.status) { "paid" -> "Lunas" "partial" -> "Cicilan" else -> "Belum Bayar" }.uppercase(), color = when(rec.status) { "paid" -> Color(0xFF059669) "partial" -> Color(0xFFD97706) else -> Color(0xFFDC2626) }, fontSize = 10.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) ) } if (rec.status != "paid") { Row( horizontalArrangement = Arrangement.spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically ) { IconButton( onClick = { dbHelper.markReceivableAsPaid(rec.id, currentUser.name) refreshReceivables() }, modifier = Modifier .size(32.dp) .background(Color(0xFFD1FAE5), CircleShape) ) { Icon( imageVector = Icons.Default.Check, contentDescription = "Lunas", tint = Color(0xFF059669), modifier = Modifier.size(16.dp) ) } Button( onClick = { selectedReceivable = rec payAmountText = "" noteText = "" alertMessage = "" showPayDialog = true }, contentPadding = PaddingValues(horizontal = 12.dp, vertical = 4.dp), shape = RoundedCornerShape(8.dp), colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF059669)), modifier = Modifier.height(32.dp) ) { Text("Bayar", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } } } // Installment Logs list AnimatedVisibility(visible = isExpanded) { Column(modifier = Modifier.padding(top = 12.dp)) { Divider(color = Color(0xFFE5E7EB)) Spacer(modifier = Modifier.height(8.dp)) Text("Riwayat Cicilan:", fontSize = 12.sp, fontWeight = FontWeight.Bold, color = Color.Gray) val payments = remember(rec.id) { dbHelper.getReceivablePayments(rec.id) } if (payments.isEmpty()) { Text("Belum ada cicilan", fontSize = 11.sp, color = Color.LightGray, modifier = Modifier.padding(vertical = 4.dp)) } else { payments.forEach { pay -> Row( modifier = Modifier .fillMaxWidth() .padding(vertical = 4.dp), horizontalArrangement = Arrangement.SpaceBetween ) { Column { Text(pay.note ?: "Cicilan", fontSize = 12.sp, fontWeight = FontWeight.Medium) Text(pay.paymentDate, fontSize = 10.sp, color = Color.Gray) } Text(formatRupiah(pay.payAmount), fontSize = 12.sp, fontWeight = FontWeight.Bold, color = Color(0xFF059669)) } } } } } } } } } } } // --- PAY INSTALLMENT DIALOG --- if (showPayDialog && selectedReceivable != null) { val remaining = selectedReceivable!!.totalAmount - selectedReceivable!!.paidAmount Dialog(onDismissRequest = { showPayDialog = false }) { Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color.White), modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Column( modifier = Modifier.padding(24.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { Text( text = "Bayar Cicilan Piutang", fontSize = 18.sp, fontWeight = FontWeight.Bold, color = Color(0xFF1F2937), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center ) Text( text = "Pelanggan: ${selectedReceivable!!.customerName}\nSisa Piutang: ${formatRupiah(remaining)}", fontSize = 13.sp, color = Color.DarkGray ) if (alertMessage.isNotEmpty()) { Text(alertMessage, color = Color.Red, fontSize = 13.sp) } OutlinedTextField( value = payAmountText, onValueChange = { payAmountText = it }, label = { Text("Jumlah Bayar (Rp)") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) OutlinedTextField( value = noteText, onValueChange = { noteText = it }, label = { Text("Catatan / Keterangan") }, singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { OutlinedButton( onClick = { showPayDialog = false }, modifier = Modifier.weight(1f) ) { Text("Batal") } Button( onClick = { if (payAmountText.isBlank()) { alertMessage = "Jumlah bayar harus diisi!" return@Button } val amount = payAmountText.toDoubleOrNull() ?: 0.0 if (amount <= 0) { alertMessage = "Jumlah bayar tidak valid!" return@Button } if (amount > remaining) { alertMessage = "Pembayaran melebihi sisa piutang!" return@Button } val success = dbHelper.payReceivableInstallment( receivableId = selectedReceivable!!.id, amount = amount, note = noteText.trim().ifEmpty { "Pembayaran cicilan" }, paidBy = currentUser.name ) if (success) { refreshReceivables() showPayDialog = false } else { alertMessage = "Gagal memproses cicilan!" } }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF059669)), modifier = Modifier.weight(1f) ) { Text("Simpan") } } } } } } // --- ADD MANUAL RECEIVABLE DIALOG --- if (showAddManualDialog) { Dialog(onDismissRequest = { showAddManualDialog = 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 = "Tambah Piutang Manual", fontSize = 18.sp, fontWeight = FontWeight.Bold, color = Color(0xFF1F2937), modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Center ) } if (manualAlertMessage.isNotEmpty()) { item { Text(manualAlertMessage, color = Color.Red, fontSize = 13.sp) } } item { OutlinedTextField( value = manualCustomerName, onValueChange = { manualCustomerName = it }, label = { Text("Nama Pelanggan") }, singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = manualTotalAmountText, onValueChange = { manualTotalAmountText = it }, label = { Text("Jumlah Piutang (Rp)") }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), singleLine = true, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) } item { OutlinedTextField( value = manualNotesText, onValueChange = { manualNotesText = it }, label = { Text("Keterangan") }, singleLine = false, maxLines = 3, shape = RoundedCornerShape(10.dp), modifier = Modifier.fillMaxWidth() ) } item { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp) ) { OutlinedButton( onClick = { showAddManualDialog = false }, modifier = Modifier.weight(1f) ) { Text("Batal") } Button( onClick = { if (manualCustomerName.isBlank() || manualTotalAmountText.isBlank()) { manualAlertMessage = "Nama dan jumlah piutang harus diisi!" return@Button } val total = manualTotalAmountText.toDoubleOrNull() ?: 0.0 if (total <= 0.0) { manualAlertMessage = "Jumlah piutang tidak valid!" return@Button } val success = dbHelper.insertManualReceivable( customerName = manualCustomerName.trim(), totalAmount = total, notes = manualNotesText.trim() ) if (success) { refreshReceivables() showAddManualDialog = false } else { manualAlertMessage = "Gagal menyimpan piutang!" } }, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF4F46E5)), modifier = Modifier.weight(1f) ) { Text("Simpan") } } } } } } } }