From abd70c3c54001c8475cac74860b4d73c7b470a11 Mon Sep 17 00:00:00 2001 From: rapaf Date: Tue, 18 Aug 2026 19:49:28 +0200 Subject: [PATCH] ver 1.5 --- app/build.gradle.kts | 2 +- .../com/bartek/iwentaryzacja/MainActivity.kt | 178 ++++++++++++++++-- 2 files changed, 167 insertions(+), 13 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1317b6e..34acba4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,7 +13,7 @@ android { applicationId = "com.bartek.iwentaryzacja" minSdk = 26 targetSdk = 37 - versionCode = 1 + versionCode = 4 versionName = "1.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/bartek/iwentaryzacja/MainActivity.kt b/app/src/main/java/com/bartek/iwentaryzacja/MainActivity.kt index be75880..f514009 100644 --- a/app/src/main/java/com/bartek/iwentaryzacja/MainActivity.kt +++ b/app/src/main/java/com/bartek/iwentaryzacja/MainActivity.kt @@ -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 = _notes.asStateFlow() + private val _branchName = MutableStateFlow("") + val branchName: StateFlow = _branchName.asStateFlow() + + private val _reportDate = MutableStateFlow("") + val reportDate: StateFlow = _reportDate.asStateFlow() + + private val _isNumericOnlySearch = MutableStateFlow(true) + val isNumericOnlySearch: StateFlow = _isNumericOnlySearch.asStateFlow() + private val _updateInfo = MutableStateFlow(null) val updateInfo: StateFlow = _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() // 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(