ver 1.5
This commit is contained in:
@@ -13,7 +13,7 @@ android {
|
|||||||
applicationId = "com.bartek.iwentaryzacja"
|
applicationId = "com.bartek.iwentaryzacja"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 37
|
targetSdk = 37
|
||||||
versionCode = 1
|
versionCode = 4
|
||||||
versionName = "1.1"
|
versionName = "1.1"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
@@ -48,6 +49,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
@@ -87,6 +89,15 @@ class InventoryViewModel : ViewModel() {
|
|||||||
private val _notes = MutableStateFlow("")
|
private val _notes = MutableStateFlow("")
|
||||||
val notes: StateFlow<String> = _notes.asStateFlow()
|
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)
|
private val _updateInfo = MutableStateFlow<UpdateInfo?>(null)
|
||||||
val updateInfo: StateFlow<UpdateInfo?> = _updateInfo.asStateFlow()
|
val updateInfo: StateFlow<UpdateInfo?> = _updateInfo.asStateFlow()
|
||||||
|
|
||||||
@@ -95,6 +106,8 @@ class InventoryViewModel : ViewModel() {
|
|||||||
fun loadState(context: Context) {
|
fun loadState(context: Context) {
|
||||||
val prefs = context.getSharedPreferences("inventory_prefs", Context.MODE_PRIVATE)
|
val prefs = context.getSharedPreferences("inventory_prefs", Context.MODE_PRIVATE)
|
||||||
_notes.value = prefs.getString("notes", "") ?: ""
|
_notes.value = prefs.getString("notes", "") ?: ""
|
||||||
|
_branchName.value = prefs.getString("branch", "") ?: ""
|
||||||
|
_reportDate.value = prefs.getString("date", "") ?: ""
|
||||||
|
|
||||||
val machinesJson = prefs.getString("machines", null)
|
val machinesJson = prefs.getString("machines", null)
|
||||||
if (machinesJson != null) {
|
if (machinesJson != null) {
|
||||||
@@ -124,6 +137,8 @@ class InventoryViewModel : ViewModel() {
|
|||||||
val prefs = context.getSharedPreferences("inventory_prefs", Context.MODE_PRIVATE)
|
val prefs = context.getSharedPreferences("inventory_prefs", Context.MODE_PRIVATE)
|
||||||
val editor = prefs.edit()
|
val editor = prefs.edit()
|
||||||
editor.putString("notes", _notes.value)
|
editor.putString("notes", _notes.value)
|
||||||
|
editor.putString("branch", _branchName.value)
|
||||||
|
editor.putString("date", _reportDate.value)
|
||||||
|
|
||||||
val jsonArray = JSONArray()
|
val jsonArray = JSONArray()
|
||||||
_machines.value.forEach { machine ->
|
_machines.value.forEach { machine ->
|
||||||
@@ -143,6 +158,10 @@ class InventoryViewModel : ViewModel() {
|
|||||||
_searchQuery.value = query
|
_searchQuery.value = query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setNumericOnlySearch(isNumeric: Boolean) {
|
||||||
|
_isNumericOnlySearch.value = isNumeric
|
||||||
|
}
|
||||||
|
|
||||||
fun setNotes(context: Context, newNotes: String) {
|
fun setNotes(context: Context, newNotes: String) {
|
||||||
_notes.value = newNotes
|
_notes.value = newNotes
|
||||||
saveState(context)
|
saveState(context)
|
||||||
@@ -235,6 +254,26 @@ class InventoryViewModel : ViewModel() {
|
|||||||
// WorkbookFactory automatycznie obsługuje zarówno .xls jak i .xlsx
|
// WorkbookFactory automatycznie obsługuje zarówno .xls jak i .xlsx
|
||||||
val workbook = WorkbookFactory.create(inputStream)
|
val workbook = WorkbookFactory.create(inputStream)
|
||||||
val sheet = workbook.getSheetAt(0)
|
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>()
|
val parsedList = mutableListOf<Machine>()
|
||||||
|
|
||||||
// Dane zaczynają się od wiersza indeks 2 (po 2 wierszach nagłówka)
|
// 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
|
// Wczytujemy tylko, gdy pozycja ma nazwę i nr ewidencyjny
|
||||||
if (name.isNotEmpty() && id.isNotEmpty()) {
|
if (name.isNotEmpty() && id.isNotEmpty()) {
|
||||||
|
val isRented = status.contains("wypo", ignoreCase = true)
|
||||||
parsedList.add(
|
parsedList.add(
|
||||||
Machine(
|
Machine(
|
||||||
id = id,
|
id = id,
|
||||||
name = name,
|
name = name,
|
||||||
category = category,
|
category = category,
|
||||||
originalStatus = status
|
originalStatus = status,
|
||||||
|
isChecked = isRented // Automatycznie zaznaczamy wypożyczone
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -283,17 +324,60 @@ class MainActivity : ComponentActivity() {
|
|||||||
surface = Color(0xFF1E2638)
|
surface = Color(0xFF1E2638)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
var showSplashScreen by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
delay(2000) // Ekran startowy przez 2 sekundy
|
||||||
|
showSplashScreen = false
|
||||||
|
}
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
color = MaterialTheme.colorScheme.background
|
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
|
// 4. EKRAN GŁÓWNY
|
||||||
// ==========================================
|
// ==========================================
|
||||||
@@ -305,11 +389,41 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
|||||||
val searchQuery by viewModel.searchQuery.collectAsState()
|
val searchQuery by viewModel.searchQuery.collectAsState()
|
||||||
val filterMissing by viewModel.filterMissing.collectAsState()
|
val filterMissing by viewModel.filterMissing.collectAsState()
|
||||||
val notes by viewModel.notes.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 updateInfo by viewModel.updateInfo.collectAsState()
|
||||||
|
val isNumericOnlySearch by viewModel.isNumericOnlySearch.collectAsState()
|
||||||
|
|
||||||
var showStatusDialog by remember { mutableStateOf(false) }
|
var showStatusDialog by remember { mutableStateOf(false) }
|
||||||
var showNotesDialog by remember { mutableStateOf(false) }
|
var showNotesDialog by remember { mutableStateOf(false) }
|
||||||
var showUpdateDialog 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) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.loadState(context)
|
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
|
var list = if (searchQuery.isBlank()) machines
|
||||||
else machines.filter {
|
else machines.filter {
|
||||||
it.name.contains(searchQuery, ignoreCase = true) ||
|
if (isNumericOnlySearch) {
|
||||||
it.id.contains(searchQuery, ignoreCase = true) ||
|
it.id.contains(searchQuery, ignoreCase = true)
|
||||||
it.category.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) {
|
if (filterMissing) {
|
||||||
@@ -394,8 +512,12 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
|||||||
list
|
list
|
||||||
}
|
}
|
||||||
|
|
||||||
val checkedCount = machines.count { it.isChecked }
|
val checkedCount = machines.count {
|
||||||
val totalCount = machines.size
|
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
|
val progress = if (totalCount > 0) checkedCount.toFloat() / totalCount.toFloat() else 0f
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@@ -453,6 +575,26 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
|||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.padding(8.dp)) {
|
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(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
@@ -498,7 +640,10 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
|||||||
// Bardzo mały pasek szukania nad klawiaturą
|
// Bardzo mały pasek szukania nad klawiaturą
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = searchQuery,
|
value = searchQuery,
|
||||||
onValueChange = { viewModel.setSearchQuery(it) },
|
onValueChange = {
|
||||||
|
viewModel.setSearchQuery(it)
|
||||||
|
viewModel.setNumericOnlySearch(false)
|
||||||
|
},
|
||||||
placeholder = { Text("Szukaj...", fontSize = 12.sp, color = Color.Gray) },
|
placeholder = { Text("Szukaj...", fontSize = 12.sp, color = Color.Gray) },
|
||||||
modifier = Modifier.fillMaxWidth().height(48.dp),
|
modifier = Modifier.fillMaxWidth().height(48.dp),
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
@@ -518,9 +663,18 @@ fun InventoryScreen(viewModel: InventoryViewModel = viewModel()) {
|
|||||||
|
|
||||||
// Klawiatura numeryczna na dole
|
// Klawiatura numeryczna na dole
|
||||||
NumericKeyboard(
|
NumericKeyboard(
|
||||||
onDigitClick = { viewModel.appendDigit(it) },
|
onDigitClick = {
|
||||||
onBackspace = { viewModel.backspace() },
|
viewModel.setNumericOnlySearch(true)
|
||||||
onClear = { viewModel.clearSearch() }
|
viewModel.appendDigit(it)
|
||||||
|
},
|
||||||
|
onBackspace = {
|
||||||
|
viewModel.setNumericOnlySearch(true)
|
||||||
|
viewModel.backspace()
|
||||||
|
},
|
||||||
|
onClear = {
|
||||||
|
viewModel.setNumericOnlySearch(true)
|
||||||
|
viewModel.clearSearch()
|
||||||
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Box(
|
Box(
|
||||||
|
|||||||
Reference in New Issue
Block a user