added admin panel, some fixes

This commit is contained in:
User 2025-02-19 09:53:55 +03:00
parent 9b70fd4dfd
commit ac107e1635
10 changed files with 145 additions and 13 deletions

View File

@ -44,6 +44,12 @@ class UserServiceST(
}
return userRepository.getInfo(tokenManager.authTokenPair!!.accessToken)
}
suspend fun getInfoByLogin(login: String): Result<UserDTO>{
if (!tokenManager.hasTokens()) {
throw RuntimeException("access token is null")
}
return userRepository.getInfoByLogin(tokenManager.authTokenPair!!.accessToken, login)
}
suspend fun openDoor(code: String): Result<Int> {
return userRepository.openDoor(tokenManager.authTokenPair!!.accessToken, code = code)
}

View File

@ -83,6 +83,22 @@ class UserRepository(
result.body()
}
}
suspend fun getInfoByLogin(token: String, login: String): Result<UserDTO> = withContext(Dispatchers.IO){
runCatching {
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/users/login/$encodedLogin") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
setBody("""{ "code": "$encodedLogin" }""")
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
result.body()
}
}
suspend fun openDoor(token: String, code: Long): Result<Unit> = withContext(Dispatchers.IO) {
runCatching {
val result = Network.client.patch("$serverUrl/api/open") {

View File

@ -23,4 +23,4 @@ data class UserDTO (
@SerialName("lastVisit")
val lastVisit: String,
)
) : java.io.Serializable

View File

@ -1,5 +1,5 @@
package com.displaynone.acss.config
object Constants {
const val serverUrl = "http://192.168.1.107:8080"
const val serverUrl = "http://192.168.56.1:8086"
}

View File

@ -0,0 +1,48 @@
package com.displaynone.acss.ui.admin
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import com.displaynone.acss.R
import com.displaynone.acss.util.collectWithLifecycle
import com.displaynone.acss.util.navigateTo
import com.displaynone.acss.databinding.FragmentAdminBinding
import com.displaynone.acss.databinding.FragmentAuthBinding
class AdminFragment : Fragment(R.layout.fragment_admin) {
private var _binding: FragmentAdminBinding? = null
private val binding: FragmentAdminBinding get() = _binding!!
private val viewModel: AdminViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentAdminBinding.bind(view)
subscribeToState(view)
binding.buttonSearch.setOnClickListener{
onSearchButtonClicked()
}
}
private fun onSearchButtonClicked() {
val login = binding.editTextTextEmailAddress.text.toString()
if (login.isEmpty()) return
viewModel.getDataByLogin(login)
}
private fun subscribeToState(view: View){
viewModel.state.collectWithLifecycle(this){ state ->
if (state is AdminViewModel.State.Show){
val userDto = state.item
val bundle = Bundle().apply{
putSerializable("user", userDto)
}
navigateTo(view, R.id.action_adminFragment_to_profileFragment, bundle)
}
}
}
}

View File

@ -0,0 +1,35 @@
package com.displaynone.acss.ui.admin
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.ui.profile.ProfileViewModel.State
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class AdminViewModel: ViewModel() {
val _state = MutableStateFlow<State>(State.Loading)
val state = _state.asStateFlow()
fun getDataByLogin(login: String){
viewModelScope.launch {
UserServiceST.getInstance().getInfoByLogin(login).fold(
onSuccess = { dto ->
_state.emit(State.Show(item = dto))
},
onFailure = { error ->
error.message?.let { error(it) }
Log.e("AdminViewModel", error.message.toString()) }
)
}
}
sealed interface State {
data class Show(
val item: UserDTO
): State
data object Loading : State
}
}

View File

@ -22,14 +22,17 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
private val viewModel: AuthViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentAuthBinding.bind(view)
setupLoginButton()
viewModel.action.collectWithLifecycle(this) { action ->
when (action) {
Action.GotoProfile -> navigateTo(view, R.id.action_authFragment_to_profileFragment)
if (action is Action.GotoProfile) {
blockLoginButton() // FIXME() При двойном нажатии вылетает с ошибкой
navigateTo(view, R.id.action_authFragment_to_profileFragment)
}
}
viewModel.errorState.collectWithLifecycle(this) { errorMessage ->
@ -43,7 +46,9 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
onLoginButtonClicked(view)
}
}
private fun blockLoginButton() {
binding.next.isEnabled = false
}
private fun setupLoginButton() {
binding.login.addTextChangedListener(object : TextWatcher {
@ -79,7 +84,6 @@ class AuthFragment: Fragment(R.layout.fragment_auth) {
private fun onLoginButtonClicked(view: View) {
val login = binding.login.text.toString()
if (login.isEmpty()) return
viewModel.login(login)
}

View File

@ -37,11 +37,23 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) {
binding.scan.setOnClickListener{
viewModel.openScan()
}
val user = getUserDto()
if (user == null) {
refreshData()
waitForQRScanResult()
} else{
showData(user) // TODO() не показывать кнопки
}
subscribe()
refreshData()
waitForQRScanResult()
}
fun showData(userDTO: UserDTO){
binding.fio.text = userDTO.name
binding.position.text = userDTO.position
binding.lastEntry.text = userDTO.lastVisit
setAvatar(userDTO.photo)
}
private fun refreshData() {
Log.d("ProfileFragment", "Refreshed")
viewModel.getInfo()
@ -53,10 +65,13 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) {
view?.let { navigateTo(it, R.id.action_profileFragment_to_authFragment) } ?: throw IllegalStateException("View is null")
}
if (action is Action.GoToScan) {
view?.let { navigateTo(it, R.id.action_profileFragment_to_qrScanFragment) }?: throw IllegalStateException("View is null")
view?.let { navigateTo(it, R.id.action_profileFragment_to_adminFragment) }?: throw IllegalStateException("View is null")
}
}
}
private fun getUserDto(): UserDTO? {
return arguments?.getSerializable("user") as? UserDTO
}
private fun waitForQRScanResult() {
requireActivity().onBackPressedDispatcher.addCallback(
@ -89,10 +104,7 @@ class ProfileFragment: Fragment(R.layout.fragment_profile) {
viewModel.state.collectWithLifecycle(this) { state ->
if (state is ProfileViewModel.State.Show) {
val userDto: UserDTO = state.item
binding.fio.text = userDto.name
binding.position.text = userDto.position
binding.lastEntry.text = userDto.lastVisit
setAvatar(userDto.photo)
showData(userDto)
}
}
}

View File

@ -17,6 +17,9 @@
<action
android:id="@+id/action_profileFragment_to_qrResultFragment"
app:destination="@id/qrResultFragment" />
<action
android:id="@+id/action_profileFragment_to_adminFragment"
app:destination="@id/adminFragment" />
</fragment>
<fragment
android:id="@+id/authFragment"
@ -38,4 +41,12 @@
android:id="@+id/action_qrResultFragment_to_profileFragment"
app:destination="@id/profileFragment" />
</fragment>
<fragment
android:id="@+id/adminFragment"
android:name="com.displaynone.acss.ui.admin.AdminFragment"
android:label="AdminFragment" >
<action
android:id="@+id/action_adminFragment_to_profileFragment"
app:destination="@id/profileFragment" />
</fragment>
</navigation>