ver 1.5
This commit is contained in:
@@ -24,6 +24,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
@@ -48,6 +49,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -87,6 +89,15 @@ class InventoryViewModel : ViewModel() {
|
||||
private val _notes = MutableStateFlow("")
|
||||
val notes: StateFlow<String> = _notes.asStateFlow()
|
||||
|
||||
private val _branchName = MutableStateFlow("")
|
||||
val branchName: StateFlow<String> = _branchName.asStateFlow()
|
||||
|
||||
private val _reportDate = MutableStateFlow("")
|
||||
val reportDate: StateFlow<String> = _reportDate.asStateFlow()
|
||||
|
||||
private val _isNumericOnlySearch = MutableStateFlow(true)
|
||||
val isNumericOnlySearch: StateFlow<Boolean> = _isNumericOnlySearch.asStateFlow()
|
||||
|
||||
private val _updateInfo = MutableStateFlow<UpdateInfo?>(null)
|
||||
val updateInfo: StateFlow<UpdateInfo?> = _updateInfo.asStateFlow()
|
||||
|
||||
@@ -95,6 +106,8 @@ class InventoryViewModel : ViewModel() {
|
||||
fun loadState(context: Context) {
|
||||
val prefs = context.getSharedPreferences("inventory_prefs", Context.MODE_PRIVATE)
|
||||
_notes.value = prefs.getString("notes", "") ?: ""
|
||||
_branchName.value = prefs.getString("branch", "") ?: ""
|
||||
_reportDate.value = prefs.getString("date", "") ?: ""
|
||||
|
||||
val machinesJson = prefs.getString("machines", null)
|
||||
if (machinesJson != null) {
|
||||
@@ -124,6 +137,8 @@ class InventoryViewModel : ViewModel() {
|
||||
val prefs = context.getSharedPreferences("inventory_prefs", Context.MODE_PRIVATE)
|
||||
val editor = prefs.edit()
|
||||
editor.putString("notes", _notes.value)
|
||||
editor.putString("branch", _branchName.value)
|
||||
editor.putString("date", _reportDate.value)
|
||||
|
||||
val jsonArray = JSONArray()
|
||||
_machines.value.forEach { machine ->
|
||||
@@ -143,6 +158,10 @@ class InventoryViewModel : ViewModel() {
|
||||
_searchQuery.value = query
|
||||
}
|
||||
|
||||
fun setNumericOnlySearch(isNumeric: Boolean) {
|
||||
_isNumericOnlySearch.value = isNumeric
|
||||
}
|
||||
|
||||
fun setNotes(context: Context, newNotes: String) {
|
||||
_notes.value = newNotes
|
||||
saveState(context)
|
||||
@@ -235,6 +254,26 @@ class InventoryViewModel : ViewModel() {
|
||||
// WorkbookFactory automatycznie obsługuje zarówno .xls jak i .xlsx
|
||||
val workbook = WorkbookFactory.create(inputStream)
|
||||
val sheet = workbook.getSheetAt(0)
|
||||
|
||||
// Próba pobrania nazwy oddziału i daty z nagłówka (zazwyczaj wiersz 0 lub 1)
|
||||
try {
|
||||
val headerRow = sheet.getRow(0)
|
||||
val branch = headerRow?.getCell(0)?.toString()?.trim() ?: ""
|
||||
val date = headerRow?.getCell(4)?.toString()?.trim() ?: "" // Przykładowa kolumna dla daty
|
||||
|
||||
// Usuwamy zbędny napis "raport:lista produktow" w różnych wariantach
|
||||
val cleanedBranch = branch
|
||||
.replace(Regex("raport\\s*:", RegexOption.IGNORE_CASE), "")
|
||||
.replace(Regex("lista\\s*produktów", RegexOption.IGNORE_CASE), "")
|
||||
.replace(Regex("lista\\s*produktow", RegexOption.IGNORE_CASE), "")
|
||||
.trim()
|
||||
|
||||
_branchName.value = cleanedBranch
|
||||
_reportDate.value = date
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
val parsedList = mutableListOf<Machine>()
|
||||
|
||||
// Dane zaczynają się od wiersza indeks 2 (po 2 wierszach nagłówka)
|
||||
@@ -248,12 +287,14 @@ class InventoryViewModel : ViewModel() {
|
||||
|
||||
// Wczytujemy tylko, gdy pozycja ma nazwę i nr ewidencyjny
|
||||
if (name.isNotEmpty() && id.isNotEmpty()) {
|
||||
val isRented = status.contains("wypo", ignoreCase = true)
|
||||
parsedList.add(
|
||||
Machine(
|
||||
id = id,
|
||||
name = name,
|
||||
category = category,
|
||||
originalStatus = status
|
||||
originalStatus = status,
|
||||
isChecked = isRented // Automatycznie zaznaczamy wypożyczone
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -283,17 +324,60 @@ class MainActivity : ComponentActivity() {
|
||||
surface = Color(0xFF1E2638)
|
||||
)
|
||||
) {
|
||||
var showSplashScreen by remember { mutableStateOf(true) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(2000) // Ekran startowy przez 2 sekundy
|
||||
showSplashScreen = false
|
||||
}
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
InventoryScreen()
|
||||
if (showSplashScreen) {
|
||||
SplashScreen()
|
||||
} else {
|
||||
InventoryScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SplashScreen() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFF121824)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Inventory,
|
||||
contentDescription = null,
|
||||
tint = Color(0xFF007ACC),
|
||||
modifier = Modifier.size(100.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Text(
|
||||
text = "inwentARyzacja",
|
||||
fontSize = 32.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = Color.White
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = "System Zarządzania Sprzętem",
|
||||
fontSize = 14.sp,
|
||||
color = Color.Gray
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 4. EKRAN GŁÓWNY
|
||||
// ==========================================
|
||||
@@ -305,11 +389,41 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
||||
val searchQuery by viewModel.searchQuery.collectAsState()
|
||||
val filterMissing by viewModel.filterMissing.collectAsState()
|
||||
val notes by viewModel.notes.collectAsState()
|
||||
val branchName by viewModel.branchName.collectAsState()
|
||||
val reportDate by viewModel.reportDate.collectAsState()
|
||||
val updateInfo by viewModel.updateInfo.collectAsState()
|
||||
val isNumericOnlySearch by viewModel.isNumericOnlySearch.collectAsState()
|
||||
|
||||
var showStatusDialog by remember { mutableStateOf(false) }
|
||||
var showNotesDialog by remember { mutableStateOf(false) }
|
||||
var showUpdateDialog by remember { mutableStateOf(false) }
|
||||
var showEasterEggDialog by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(searchQuery) {
|
||||
if (searchQuery == "666666") {
|
||||
showEasterEggDialog = true
|
||||
viewModel.clearSearch()
|
||||
}
|
||||
}
|
||||
|
||||
if (showEasterEggDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showEasterEggDialog = false },
|
||||
title = { Text("O aplikacji", color = Color.White) },
|
||||
text = {
|
||||
Text(
|
||||
"Aplikacja zrobiona w całości przez Bartka J, w wolnym czasie i na prywatnych komputerach",
|
||||
color = Color.White
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showEasterEggDialog = false }) {
|
||||
Text("OK")
|
||||
}
|
||||
},
|
||||
containerColor = Color(0xFF1E2638)
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.loadState(context)
|
||||
@@ -380,12 +494,16 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
||||
)
|
||||
}
|
||||
|
||||
val filteredMachines = remember(machines, searchQuery, filterMissing) {
|
||||
val filteredMachines = remember(machines, searchQuery, filterMissing, isNumericOnlySearch) {
|
||||
var list = if (searchQuery.isBlank()) machines
|
||||
else machines.filter {
|
||||
it.name.contains(searchQuery, ignoreCase = true) ||
|
||||
it.id.contains(searchQuery, ignoreCase = true) ||
|
||||
it.category.contains(searchQuery, ignoreCase = true)
|
||||
if (isNumericOnlySearch) {
|
||||
it.id.contains(searchQuery, ignoreCase = true)
|
||||
} else {
|
||||
it.name.contains(searchQuery, ignoreCase = true) ||
|
||||
it.id.contains(searchQuery, ignoreCase = true) ||
|
||||
it.category.contains(searchQuery, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
if (filterMissing) {
|
||||
@@ -394,8 +512,12 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
||||
list
|
||||
}
|
||||
|
||||
val checkedCount = machines.count { it.isChecked }
|
||||
val totalCount = machines.size
|
||||
val checkedCount = machines.count {
|
||||
it.isChecked && !it.originalStatus.contains("wypo", ignoreCase = true)
|
||||
}
|
||||
val totalCount = machines.count {
|
||||
!it.originalStatus.contains("wypo", ignoreCase = true)
|
||||
}
|
||||
val progress = if (totalCount > 0) checkedCount.toFloat() / totalCount.toFloat() else 0f
|
||||
|
||||
Scaffold(
|
||||
@@ -453,6 +575,26 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Column(modifier = Modifier.padding(8.dp)) {
|
||||
if (branchName.isNotEmpty() || reportDate.isNotEmpty()) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = branchName,
|
||||
color = Color(0xFF11CAA0),
|
||||
fontSize = 11.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1
|
||||
)
|
||||
Text(
|
||||
text = reportDate,
|
||||
color = Color.Gray,
|
||||
fontSize = 10.sp
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
@@ -498,7 +640,10 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
||||
// Bardzo mały pasek szukania nad klawiaturą
|
||||
OutlinedTextField(
|
||||
value = searchQuery,
|
||||
onValueChange = { viewModel.setSearchQuery(it) },
|
||||
onValueChange = {
|
||||
viewModel.setSearchQuery(it)
|
||||
viewModel.setNumericOnlySearch(false)
|
||||
},
|
||||
placeholder = { Text("Szukaj...", fontSize = 12.sp, color = Color.Gray) },
|
||||
modifier = Modifier.fillMaxWidth().height(48.dp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
@@ -518,9 +663,18 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
||||
|
||||
// Klawiatura numeryczna na dole
|
||||
NumericKeyboard(
|
||||
onDigitClick = { viewModel.appendDigit(it) },
|
||||
onBackspace = { viewModel.backspace() },
|
||||
onClear = { viewModel.clearSearch() }
|
||||
onDigitClick = {
|
||||
viewModel.setNumericOnlySearch(true)
|
||||
viewModel.appendDigit(it)
|
||||
},
|
||||
onBackspace = {
|
||||
viewModel.setNumericOnlySearch(true)
|
||||
viewModel.backspace()
|
||||
},
|
||||
onClear = {
|
||||
viewModel.setNumericOnlySearch(true)
|
||||
viewModel.clearSearch()
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
|
||||
Reference in New Issue
Block a user