1 Commits
4 changed files with 102 additions and 57 deletions

No files matched your search

+2 -2
View File
@@ -30,8 +30,8 @@ android {
applicationId = "com.oqba26.monthlypaymentapp" applicationId = "com.oqba26.monthlypaymentapp"
minSdk = 25 minSdk = 25
targetSdk = 34 targetSdk = 34
versionCode = 9 versionCode = 10
versionName = "1.0.8" versionName = "1.0.9"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { useSupportLibrary = true } vectorDrawables { useSupportLibrary = true }
} }
@@ -1117,10 +1117,6 @@ fun DashboardCard(
if (financialReportData.totalOverdueDebt > 0 || financialReportData.debtors.isNotEmpty()) { if (financialReportData.totalOverdueDebt > 0 || financialReportData.debtors.isNotEmpty()) {
HorizontalDivider(modifier = Modifier.padding(vertical = 2.dp)) HorizontalDivider(modifier = Modifier.padding(vertical = 2.dp))
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
@@ -1139,19 +1135,6 @@ fun DashboardCard(
color = MaterialTheme.colorScheme.error color = MaterialTheme.colorScheme.error
) )
} }
val debtorNamesText = financialReportData.debtors.joinToString("، ") {
"${it.personName} (${formatNumberAsPersian(it.totalDebtAmount)})"
}
Text(
text = "بدهکاران (${financialReportData.totalDebtorsCount.toString().toPersianDigits()} نفر): $debtorNamesText",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 3,
overflow = TextOverflow.Ellipsis
)
}
} }
if (!isOperatorMode) { if (!isOperatorMode) {
@@ -1237,6 +1220,34 @@ fun FinancialReportDialog(
} }
} }
if (data.debtors.isNotEmpty()) {
Spacer(modifier = Modifier.height(8.dp))
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.15f))
) {
Column(
modifier = Modifier.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "لیست بدهکاران (${data.totalDebtorsCount.toString().toPersianDigits()} نفر):",
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.error
)
val debtorNamesText = data.debtors.joinToString("، ") {
"${it.personName} (${formatNumberAsPersian(it.totalDebtAmount)})"
}
Text(
text = debtorNamesText,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
HorizontalDivider() HorizontalDivider()
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
@@ -5,24 +5,32 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Logout import androidx.compose.material.icons.automirrored.filled.Logout
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.AdminPanelSettings
import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.ContentCopy import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.LockOpen
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.PasswordVisualTransformation
@@ -136,16 +144,18 @@ fun SettingsScreen(onLogout: () -> Unit) {
} }
) )
val scrollState = rememberScrollState()
Column( Column(
modifier = Modifier modifier = Modifier
.padding(horizontal = 16.dp, vertical = 8.dp) .fillMaxSize()
.fillMaxWidth() .verticalScroll(scrollState)
.fillMaxHeight() .padding(horizontal = 16.dp, vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) { ) {
Text( Text(
stringResource(R.string.settings), stringResource(R.string.settings),
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium
modifier = Modifier.padding(bottom = 8.dp)
) )
DefaultAmountSetting( DefaultAmountSetting(
@@ -163,16 +173,12 @@ fun SettingsScreen(onLogout: () -> Unit) {
onChangePin = { viewModel.setAdminPin(it) } onChangePin = { viewModel.setAdminPin(it) }
) )
Spacer(Modifier.weight(1f))
val reminderDay by viewModel.reminderDay.collectAsState() val reminderDay by viewModel.reminderDay.collectAsState()
ReminderDaySetting( ReminderDaySetting(
currentDay = reminderDay, currentDay = reminderDay,
onSave = { viewModel.saveReminderDay(it) } onSave = { viewModel.saveReminderDay(it) }
) )
Spacer(Modifier.weight(1f))
val cardNumbers by viewModel.cardNumbers.collectAsState() val cardNumbers by viewModel.cardNumbers.collectAsState()
CardNumbersSetting( CardNumbersSetting(
cardNumbers = cardNumbers, cardNumbers = cardNumbers,
@@ -181,20 +187,17 @@ fun SettingsScreen(onLogout: () -> Unit) {
onRemove = { viewModel.removeCardNumber(it) } onRemove = { viewModel.removeCardNumber(it) }
) )
Spacer(Modifier.weight(1f))
FontSelectionDropdown( FontSelectionDropdown(
selectedFont = selectedFont, selectedFont = selectedFont,
onFontSelected = { viewModel.onFontSelected(it) } onFontSelected = { viewModel.onFontSelected(it) }
) )
if (!isOperatorMode) { if (!isOperatorMode) {
HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp)) HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
Text( Text(
"پشتیبان‌گیری و بازیابی", "پشتیبان‌گیری و بازیابی",
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium
modifier = Modifier.padding(bottom = 8.dp)
) )
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
@@ -224,24 +227,26 @@ fun SettingsScreen(onLogout: () -> Unit) {
viewModel.loadSnapshots() viewModel.loadSnapshots()
showSnapshotDialog = true showSnapshotDialog = true
}, },
modifier = Modifier.fillMaxWidth().padding(top = 8.dp) modifier = Modifier.fillMaxWidth()
) { ) {
Text(stringResource(R.string.auto_backup_button), fontSize = 13.sp) Text(stringResource(R.string.auto_backup_button), fontSize = 13.sp)
} }
} }
Spacer(Modifier.weight(1f)) Spacer(Modifier.height(8.dp))
Button( Button(
onClick = onLogout, onClick = onLogout,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.error), colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.error),
contentPadding = androidx.compose.foundation.layout.PaddingValues(vertical = 8.dp) contentPadding = PaddingValues(vertical = 12.dp)
) { ) {
Icon(Icons.AutoMirrored.Filled.Logout, contentDescription = null) Icon(Icons.AutoMirrored.Filled.Logout, contentDescription = null)
Spacer(Modifier.width(8.dp)) Spacer(Modifier.width(8.dp))
Text(stringResource(R.string.logout)) Text(stringResource(R.string.logout))
} }
Spacer(Modifier.height(16.dp))
} }
if (showSnapshotDialog) { if (showSnapshotDialog) {
@@ -621,23 +626,45 @@ fun OperatorModeSetting(
Card( Card(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = if (isOperatorMode) MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.2f) else MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.2f) containerColor = if (isOperatorMode) MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.15f) else MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
) )
) { ) {
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp)) { Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Column(modifier = Modifier.weight(1f)) { Icon(
Text("حالت دسترسی برنامه", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold) imageVector = if (isOperatorMode) Icons.Default.Lock else Icons.Default.AdminPanelSettings,
contentDescription = null,
tint = if (isOperatorMode) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Column {
Text( Text(
if (isOperatorMode) "وضعیت فعلی: حالت اپراتور (محدود)" else "وضعیت فعلی: حالت مدیریت (کامل)", text = "حالت دسترسی برنامه",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
)
Text(
text = if (isOperatorMode) "وضعیت فعلی: حالت اپراتور (محدود)" else "وضعیت فعلی: حالت مدیریت (کامل)",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = if (isOperatorMode) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary color = if (isOperatorMode) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary
) )
} }
} }
}
HorizontalDivider(thickness = 0.5.dp)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth()) {
if (isOperatorMode) { if (isOperatorMode) {
Button( Button(
onClick = { onClick = {
@@ -646,18 +673,24 @@ fun OperatorModeSetting(
errorMessage = "" errorMessage = ""
showPinDialog = true showPinDialog = true
}, },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(10.dp)
) { ) {
Text("خروج از حالت اپراتور") Icon(Icons.Default.LockOpen, contentDescription = null, modifier = Modifier.size(18.dp))
Spacer(modifier = Modifier.width(6.dp))
Text("خروج از حالت اپراتور (ورود رمز مدیر)")
} }
} else { } else {
Button( Row(
onClick = { horizontalArrangement = Arrangement.spacedBy(8.dp),
onToggleMode(true) modifier = Modifier.fillMaxWidth()
},
modifier = Modifier.weight(1f)
) { ) {
Text("فعال‌سازی حالت اپراتور") Button(
onClick = { onToggleMode(true) },
modifier = Modifier.weight(1f),
shape = RoundedCornerShape(10.dp)
) {
Text("فعال‌سازی حالت اپراتور", style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold)
} }
OutlinedButton( OutlinedButton(
@@ -668,9 +701,10 @@ fun OperatorModeSetting(
errorMessage = "" errorMessage = ""
showPinDialog = true showPinDialog = true
}, },
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f),
shape = RoundedCornerShape(10.dp)
) { ) {
Text("تغییر رمز مدیر") Text("تغییر رمز مدیر", style = MaterialTheme.typography.bodyMedium, fontWeight = FontWeight.Bold)
} }
} }
} }
+4 -4
View File
@@ -1,7 +1,7 @@
{ {
"versionCode": 9, "versionCode": 10,
"versionName": "1.0.8", "versionName": "1.0.9",
"url": "https://github.com/oqba26/MonthlyPaymentApp/releases/download/v1.0.8/MonthlyPaymentApp-v1.0.8.apk", "url": "https://github.com/oqba26/MonthlyPaymentApp/releases/download/v1.0.9/MonthlyPaymentApp-v1.0.9.apk",
"releaseNotes": "بهبودهای تب مسجد و افزودن حالت اپراتور (محدودسازی دسترسی)", "releaseNotes": "اصلاح اسکرول صفحه تنظیمات، بهبود ظاهری کارت حالت دسترسی و انتقال لیست اسامی بدهکاران به گزارش مالی",
"isForceUpdate": false "isForceUpdate": false
} }