feat: Информация о пользователе + фиксы багов

This commit is contained in:
yastruckov 2025-02-20 14:59:33 +03:00
parent eb4a59e4fe
commit 8cd87144b8
13 changed files with 117 additions and 23 deletions

View File

@ -39,15 +39,6 @@ class EmployeeInfoNetworkDataSource(
HttpStatusCode.NotFound -> error(context.getString(R.string.not_found))
HttpStatusCode.OK -> result.body()
}
if(result.status == HttpStatusCode.Unauthorized){
error(context.getString(R.string.admin_unauthorized))
}
if (result.status != HttpStatusCode.OK) {
println(result.status)
error("Status ${result.status}")
}
println(result.bodyAsText())
result.body()
}

View File

@ -18,11 +18,11 @@ class ScannerStateNetworkDataSource(
) {
private val client = NetworkModule.httpClient
private val userDataStoreManager = UserDataStoreManager.getInstance(context)
suspend fun setScannerState(state: String, login: String):Result<Unit> = withContext(Dispatchers.IO){
suspend fun setScannerState(login: String):Result<Unit> = withContext(Dispatchers.IO){
runCatching {
val username = userDataStoreManager.usernameFlow.first()
val password = userDataStoreManager.passwordFlow.first()
val result = client.patch("${Constants.SERVER_ADDRESS}/api/employee/$login/$state"){
val result = client.patch("${Constants.SERVER_ADDRESS}/api/employee/$login/change_state"){
headers{
basicAuth(username, password)
}

View File

@ -5,7 +5,7 @@ import ru.myitschool.work.domain.scannerBlockState.ScannerStateRepo
class ScannerStateRepoImpl(
private val networkDataSource: ScannerStateNetworkDataSource
) : ScannerStateRepo {
override suspend fun changeState(state: String, login: String) : Result<Unit> {
return networkDataSource.setScannerState(state, login)
override suspend fun changeState(login: String) : Result<Unit> {
return networkDataSource.setScannerState(login)
}
}

View File

@ -1,5 +1,5 @@
package ru.myitschool.work.domain.scannerBlockState
interface ScannerStateRepo {
suspend fun changeState(state: String, login: String) : Result<Unit>
suspend fun changeState(login: String) : Result<Unit>
}

View File

@ -3,5 +3,5 @@ package ru.myitschool.work.domain.scannerBlockState
class SetScannerStateUseCase(
private val repo: ScannerStateRepo
) {
suspend operator fun invoke(state: String, login: String) = repo.changeState(state, login)
suspend operator fun invoke(login: String) = repo.changeState(login)
}

View File

@ -8,11 +8,15 @@ import android.view.View
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.squareup.picasso.Picasso
import ru.myitschool.work.R
import ru.myitschool.work.databinding.FragmentAdminBinding
import ru.myitschool.work.entities.EmployeeEntity
import ru.myitschool.work.ui.login.LoginViewModel
import ru.myitschool.work.utils.buttonRecolor
import ru.myitschool.work.utils.collectWithLifecycle
class AdminFragment : Fragment(R.layout.fragment_admin) {
private var _binding: FragmentAdminBinding? = null
private val binding get() = _binding!!
@ -37,6 +41,7 @@ class AdminFragment : Fragment(R.layout.fragment_admin) {
val isEnabled =
search.length >= 3 && !search[0].isDigit() && search.matches(Regex("^[a-zA-Z0-9]*$"))
binding.searchBtn.isEnabled = isEnabled
@Suppress("DEPRECATION")
if (isEnabled) {
binding.searchBtn.setBackgroundColor(resources.getColor(R.color.accent_color))
binding.searchBtn.imageTintList = ColorStateList.valueOf(
@ -52,31 +57,63 @@ class AdminFragment : Fragment(R.layout.fragment_admin) {
R.color.secondary_text_color
))
}
}
}
binding.search.addTextChangedListener(textWatcher)
viewModel.infoState.collectWithLifecycle(this){ state ->
when(state){
is AdminViewModel.SearchState.Error -> {
binding.error.visibility = View.VISIBLE
binding.error.text = state.message
binding.userInfo.visibility = View.GONE
}
AdminViewModel.SearchState.Loading -> {
binding.error.visibility = View.GONE
binding
binding.userInfo.visibility = View.GONE
}
is AdminViewModel.SearchState.Success -> {
binding.error.visibility = View.GONE
binding.userInfo.visibility = View.VISIBLE
showUserData(state.data)
}
}
}
binding.blockBtn.setOnClickListener {
viewModel.changeState(binding.search.text.toString())
}
viewModel.blockState.collectWithLifecycle(this){ state ->
when(state){
is AdminViewModel.BlockState.Error -> {
binding.error.visibility = View.VISIBLE
binding.error.text = state.message
}
AdminViewModel.BlockState.Loading -> {
binding.error.visibility = View.GONE
binding.userInfo.visibility = View.GONE
}
AdminViewModel.BlockState.Success -> {
binding.error.visibility = View.GONE
binding.userInfo.visibility = View.VISIBLE
}
}
}
}
private fun showUserData(user: EmployeeEntity){
binding.userName.text = user.name
binding.position.text = user.position
if(user.qrEnabled){
binding.blockBtn.text = ContextCompat.getString(requireContext(), R.string.block_btn)
buttonRecolor(requireContext(), binding.blockBtn, R.color.accent_color, R.color.white )
}
else{
binding.blockBtn.text = ContextCompat.getString(requireContext(), R.string.unblock_btn)
buttonRecolor(requireContext(), binding.blockBtn, R.color.bg_color, R.color.secondary_text_color )
}
Picasso.get().load(user.photoUrl).into(binding.avatar)
}
}

View File

@ -12,22 +12,34 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import ru.myitschool.work.data.profile.admin.EmployeeInfoNetworkDataSource
import ru.myitschool.work.data.profile.admin.EmployeeInfoRepoImpl
import ru.myitschool.work.data.scannerState.ScannerStateNetworkDataSource
import ru.myitschool.work.data.scannerState.ScannerStateRepoImpl
import ru.myitschool.work.domain.profile.admin.EmployeeInfoRepo
import ru.myitschool.work.domain.profile.admin.GetEmployeeInfoUseCase
import ru.myitschool.work.domain.scannerBlockState.SetScannerStateUseCase
import ru.myitschool.work.entities.EmployeeEntity
class AdminViewModel(
private val getInfoUseCase: GetEmployeeInfoUseCase,
private val setScannerStateUseCase : SetScannerStateUseCase,
application: Application
) : AndroidViewModel(application) {
private val _infoState = MutableStateFlow<SearchState>(SearchState.Loading)
val infoState: StateFlow<SearchState> = _infoState.asStateFlow()
private val _blockState = MutableStateFlow<BlockState>(BlockState.Loading)
val blockState: StateFlow<BlockState> = _blockState.asStateFlow()
sealed class SearchState {
data object Loading : SearchState()
data class Success(val data: EmployeeEntity) : SearchState()
data class Error(val message: String?) : SearchState()
}
sealed class BlockState {
data object Loading : BlockState()
data object Success : BlockState()
data class Error(val message: String?) : BlockState()
}
fun searchUser(login : String){
_infoState.value = SearchState.Loading
viewModelScope.launch {
@ -41,6 +53,20 @@ class AdminViewModel(
)
}
}
fun changeState(login: String){
viewModelScope.launch {
_blockState.value = BlockState.Loading
setScannerStateUseCase.invoke(login).fold(
onSuccess = { _ ->
_blockState.value = BlockState.Success
},
onFailure = { e ->
_blockState.value = BlockState.Error(e.message)
}
)
}
searchUser(login)
}
companion object {
@Suppress("UNCHECKED_CAST")
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@ -50,10 +76,16 @@ class AdminViewModel(
context = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
)
)
val useCase = GetEmployeeInfoUseCase(infoRepoImpl)
val setScannerStateRepoImpl = ScannerStateRepoImpl(
networkDataSource = ScannerStateNetworkDataSource(
context = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
)
)
val getInfoUseCase = GetEmployeeInfoUseCase(infoRepoImpl)
val setScannerStateUseCase = SetScannerStateUseCase(setScannerStateRepoImpl)
return AdminViewModel(
useCase, extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
getInfoUseCase, setScannerStateUseCase, extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
) as T
}
}

View File

@ -39,9 +39,9 @@ class MainFragment : Fragment(R.layout.fragment_main) {
viewModel.getUserData()
viewModel.getLastEntryDate()
findNavController().navigate(R.id.admin)
binding.logout.setOnClickListener { logout() }
binding.scan.setOnClickListener { onScanClick() }
binding.admin.setOnClickListener { findNavController().navigate(R.id.admin) }
viewModel.userState.collectWhenStarted(this) { state ->
when (state) {
is UserState.Error -> {

View File

@ -0,0 +1,21 @@
package ru.myitschool.work.utils
import android.content.Context
import android.content.res.ColorStateList
import android.widget.Button
import androidx.core.content.ContextCompat
import ru.myitschool.work.R
fun buttonRecolor(
context: Context,
btn: Button,
bgColor: Int,
textColor: Int
) {
btn.backgroundTintList = ColorStateList.valueOf(
ContextCompat.getColor(
context,
bgColor
))
btn.setTextColor(context.getColor(textColor))
}

View File

@ -78,6 +78,7 @@
app:layout_constraintVertical_bias="0.0">
<LinearLayout
android:id="@+id/userInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"

View File

@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_color">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_left"
android:layout_width="wrap_content"
@ -18,7 +18,7 @@
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="16dp"/>
<LinearLayout
android:id="@+id/linear"
android:layout_width="0dp"
@ -150,6 +150,12 @@
app:layout_constraintLeft_toRightOf="@+id/guideline_left"
app:layout_constraintRight_toLeftOf="@+id/guideline_right">
<Button
android:id="@+id/admin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Admin" />
<TextView
android:id="@+id/position"
style="@style/font_medium"

View File

@ -20,4 +20,7 @@
<string name="search_hint">Enter the employee\'s username</string>
<string name="block_btn">Block</string>
<string name="unblock_btn">Unblock</string>
<string name="admin_unauthorized">Вы не авторизованы</string>
<string name="admin_forbidden">Нет доступа</string>
<string name="not_found">404\nEmployee not found</string>
</resources>

View File

@ -22,6 +22,9 @@
<string name="search_hint">Введите логин сотрудника</string>
<string name="block_btn">Заблокировать</string>
<string name="unblock_btn">Разблокировать</string>
<string name="admin_unauthorized">Вы не авторизованы</string>
<string name="admin_forbidden">Нет доступа</string>
<string name="not_found">404\nСотрудник не найден</string>
<!-- TODO: Remove or change this placeholder text -->