feat: admin section done
This commit is contained in:
parent
f76d744fd7
commit
9393cb89b0
@ -0,0 +1,14 @@
|
|||||||
|
package ru.myitschool.work.data
|
||||||
|
|
||||||
|
import ru.myitschool.work.data.local.CredentialsLocalDataSource
|
||||||
|
import ru.myitschool.work.data.network.AdminNetworkDataSource
|
||||||
|
import ru.myitschool.work.domain.admin.AdminRepository
|
||||||
|
|
||||||
|
class AdminRepositoryImpl(
|
||||||
|
private val networkDataSource: AdminNetworkDataSource,
|
||||||
|
private val localCredentialsLocalDataSource: CredentialsLocalDataSource
|
||||||
|
): AdminRepository {
|
||||||
|
override suspend fun blockUser(login: String): Result<Unit> {
|
||||||
|
return networkDataSource.blockUser(login, localCredentialsLocalDataSource.getToken())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package ru.myitschool.work.data.network
|
||||||
|
|
||||||
|
import io.ktor.client.request.headers
|
||||||
|
import io.ktor.client.request.patch
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import ru.myitschool.work.core.Constants.SERVER_ADDRESS
|
||||||
|
|
||||||
|
object AdminNetworkDataSource {
|
||||||
|
|
||||||
|
private val client = KtorClient.client
|
||||||
|
|
||||||
|
suspend fun blockUser(login: String, token: String): Result<Unit> =
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
runCatching {
|
||||||
|
val response = client.patch("$SERVER_ADDRESS/api/employees/block&login=${login}") {
|
||||||
|
headers {
|
||||||
|
append(HttpHeaders.Authorization, token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.status != HttpStatusCode.OK)
|
||||||
|
error("Status ${response.status}")
|
||||||
|
Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package ru.myitschool.work.domain.admin
|
||||||
|
|
||||||
|
interface AdminRepository {
|
||||||
|
suspend fun blockUser(login: String): Result<Unit>
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package ru.myitschool.work.domain.admin
|
||||||
|
|
||||||
|
class BlockUserUseCase(
|
||||||
|
private val repository: AdminRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend operator fun invoke(login: String) = repository.blockUser(login)
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package ru.myitschool.work.domain.passes
|
||||||
|
|
||||||
|
class GetUsersPassesUseCase(
|
||||||
|
private val repository: PassRepository
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend operator fun invoke(pageNum: Int, pageSize: Int, login: String) =
|
||||||
|
repository.getUsersPasses(pageNum, pageSize, login)
|
||||||
|
}
|
@ -4,13 +4,18 @@ import android.os.Bundle
|
|||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import ru.myitschool.work.R
|
import ru.myitschool.work.R
|
||||||
|
import ru.myitschool.work.utils.isOnline
|
||||||
|
|
||||||
// НЕ ИЗМЕНЯЙТЕ НАЗВАНИЕ КЛАССА!
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class RootActivity : AppCompatActivity() {
|
class RootActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_root)
|
setContentView(R.layout.activity_root)
|
||||||
|
|
||||||
|
if (!isOnline(this)) {
|
||||||
|
val dialog = NoInternetNotificationFragment()
|
||||||
|
dialog.isCancelable = false
|
||||||
|
dialog.show(supportFragmentManager, "NO_INTERNET")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package ru.myitschool.work.ui.admin.search
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import ru.myitschool.work.R
|
||||||
|
import ru.myitschool.work.databinding.FragmentFindEmployeeBinding
|
||||||
|
import ru.myitschool.work.utils.collectWithLifecycle
|
||||||
|
|
||||||
|
class AdminFragment : Fragment(R.layout.fragment_find_employee) {
|
||||||
|
|
||||||
|
private var _binding: FragmentFindEmployeeBinding? = null
|
||||||
|
private val binding: FragmentFindEmployeeBinding get() = _binding!!
|
||||||
|
|
||||||
|
private val viewModel by viewModels<AdminViewModel> { AdminViewModel.Factory }
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
_binding = FragmentFindEmployeeBinding.bind(view)
|
||||||
|
|
||||||
|
viewModel.state.collectWithLifecycle(this) { state ->
|
||||||
|
binding.username.isEnabled = state !is AdminViewModel.State.Loading
|
||||||
|
when (state) {
|
||||||
|
is AdminViewModel.State.Error -> binding.error.text = state.errorMessage
|
||||||
|
is AdminViewModel.State.Loading -> Unit
|
||||||
|
is AdminViewModel.State.Show -> {}
|
||||||
|
is AdminViewModel.State.Waiting -> Unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.find.setOnClickListener {
|
||||||
|
viewModel.onFind(binding.username.text.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
_binding = null
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
package ru.myitschool.work.ui.admin.search
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.cachedIn
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.myitschool.work.data.AdminRepositoryImpl
|
||||||
|
import ru.myitschool.work.data.PassRepositoryImpl
|
||||||
|
import ru.myitschool.work.data.UserRepositoryImpl
|
||||||
|
import ru.myitschool.work.data.local.CredentialsLocalDataSource
|
||||||
|
import ru.myitschool.work.data.local.UserLocalDataSource
|
||||||
|
import ru.myitschool.work.data.network.AdminNetworkDataSource
|
||||||
|
import ru.myitschool.work.data.network.PassNetworkDataSource
|
||||||
|
import ru.myitschool.work.data.network.UserNetworkDataSource
|
||||||
|
import ru.myitschool.work.domain.admin.BlockUserUseCase
|
||||||
|
import ru.myitschool.work.domain.entities.PassEntity
|
||||||
|
import ru.myitschool.work.domain.entities.UserEntity
|
||||||
|
import ru.myitschool.work.domain.passes.GetUsersPassesUseCase
|
||||||
|
import ru.myitschool.work.domain.user.GetUserByLoginUseCase
|
||||||
|
import ru.myitschool.work.ui.admin.view.UsersPassesPagingSource
|
||||||
|
|
||||||
|
class AdminViewModel(
|
||||||
|
private val getUserByLoginUseCase: GetUserByLoginUseCase,
|
||||||
|
private val getUsersPassesUseCase: GetUsersPassesUseCase,
|
||||||
|
private val blockUserUseCase: BlockUserUseCase
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow<State>(State.Waiting)
|
||||||
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
|
private var _listState: Flow<PagingData<PassEntity>>? = null
|
||||||
|
val listState: Flow<PagingData<PassEntity>> get() = _listState!!
|
||||||
|
|
||||||
|
private var currentLogin: String? = null
|
||||||
|
|
||||||
|
fun onFind(login: String) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_state.emit(State.Loading)
|
||||||
|
getUserByLoginUseCase(login).fold(
|
||||||
|
onSuccess = { data ->
|
||||||
|
currentLogin = login
|
||||||
|
_state.emit(State.Show(data))
|
||||||
|
setUpPager(login)
|
||||||
|
},
|
||||||
|
onFailure = { _state.emit(State.Error(it.message.toString())) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onRefresh() {
|
||||||
|
updateState()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateState() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
_state.emit(State.Loading)
|
||||||
|
getUserByLoginUseCase(currentLogin!!).fold(
|
||||||
|
onSuccess = { _state.emit(State.Show(it)) },
|
||||||
|
onFailure = { _state.emit(State.Error(it.message.toString())) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpPager(login: String) {
|
||||||
|
_listState = Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = 10,
|
||||||
|
enablePlaceholders = false,
|
||||||
|
maxSize = 50
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
UsersPassesPagingSource(getUsersPassesUseCase::invoke, login)
|
||||||
|
}.flow
|
||||||
|
.cachedIn(viewModelScope)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onBlock() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
blockUserUseCase(currentLogin!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface State {
|
||||||
|
data class Show(val user: UserEntity) : State
|
||||||
|
data object Waiting : State
|
||||||
|
data object Loading : State
|
||||||
|
data class Error(val errorMessage: String) : State
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
||||||
|
return AdminViewModel(
|
||||||
|
getUserByLoginUseCase = GetUserByLoginUseCase(
|
||||||
|
repository = UserRepositoryImpl(
|
||||||
|
credentialsLocalDataSource = CredentialsLocalDataSource.getInstance(),
|
||||||
|
userLocalDataSource = UserLocalDataSource,
|
||||||
|
networkDataSource = UserNetworkDataSource
|
||||||
|
)
|
||||||
|
),
|
||||||
|
getUsersPassesUseCase = GetUsersPassesUseCase(
|
||||||
|
repository = PassRepositoryImpl(
|
||||||
|
networkDataSource = PassNetworkDataSource,
|
||||||
|
credentialsLocalDataSource = CredentialsLocalDataSource.getInstance()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
blockUserUseCase = BlockUserUseCase(
|
||||||
|
repository = AdminRepositoryImpl(
|
||||||
|
networkDataSource = AdminNetworkDataSource,
|
||||||
|
localCredentialsLocalDataSource = CredentialsLocalDataSource.getInstance()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) as T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package ru.myitschool.work.ui.admin.view
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import ru.myitschool.work.domain.entities.PassEntity
|
||||||
|
|
||||||
|
class UsersPassesPagingSource(
|
||||||
|
private val request: suspend (pageNum: Int, pageSize: Int, login: String) -> Result<List<PassEntity>>,
|
||||||
|
private val login: String
|
||||||
|
) : PagingSource<Int, PassEntity>() {
|
||||||
|
override fun getRefreshKey(state: PagingState<Int, PassEntity>): Int? {
|
||||||
|
return state.anchorPosition?.let {
|
||||||
|
state.closestPageToPosition(it)?.prevKey?.plus(1)
|
||||||
|
?: state.closestPageToPosition(it)?.nextKey?.minus(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, PassEntity> {
|
||||||
|
val pageNum = params.key ?: 0
|
||||||
|
return request.invoke(pageNum, params.loadSize, login).fold(
|
||||||
|
onSuccess = { value ->
|
||||||
|
LoadResult.Page(
|
||||||
|
data = value,
|
||||||
|
prevKey = (pageNum - 1).takeIf { it >= 0 },
|
||||||
|
nextKey = (pageNum + 1).takeIf { value.size == params.loadSize }
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onFailure = { error -> LoadResult.Error(error) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
package ru.myitschool.work.ui.admin.view
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.paging.LoadState
|
||||||
|
import com.squareup.picasso.Picasso
|
||||||
|
import ru.myitschool.work.R
|
||||||
|
import ru.myitschool.work.databinding.FragmentUserBinding
|
||||||
|
import ru.myitschool.work.ui.admin.search.AdminViewModel
|
||||||
|
import ru.myitschool.work.ui.profile.PassesListAdapter
|
||||||
|
import ru.myitschool.work.utils.collectWithLifecycle
|
||||||
|
import ru.myitschool.work.utils.visibleOrGone
|
||||||
|
|
||||||
|
class ViewUserAsAdminFragment : Fragment(R.layout.fragment_user) {
|
||||||
|
|
||||||
|
private var _binding: FragmentUserBinding? = null
|
||||||
|
private val binding: FragmentUserBinding get() = _binding!!
|
||||||
|
|
||||||
|
private val viewModel by viewModels<AdminViewModel> { AdminViewModel.Factory }
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
_binding = FragmentUserBinding.bind(view)
|
||||||
|
|
||||||
|
binding.findUser.visibleOrGone(false)
|
||||||
|
binding.logout.visibleOrGone(false)
|
||||||
|
binding.block.visibleOrGone(true)
|
||||||
|
|
||||||
|
val adapter = PassesListAdapter()
|
||||||
|
binding.passes.adapter = adapter
|
||||||
|
|
||||||
|
viewModel.state.collectWithLifecycle(this) { state ->
|
||||||
|
binding.refresh.isRefreshing = state is AdminViewModel.State.Loading
|
||||||
|
binding.content?.visibleOrGone(state is AdminViewModel.State.Show)
|
||||||
|
|
||||||
|
when (state) {
|
||||||
|
is AdminViewModel.State.Loading -> Unit
|
||||||
|
is AdminViewModel.State.Show -> {
|
||||||
|
val user = state.user
|
||||||
|
binding.findUser.visibleOrGone(user.isAdmin)
|
||||||
|
binding.fullname.text = user.name
|
||||||
|
binding.position.text = user.position
|
||||||
|
binding.lastEntry.text = user.lastVisit
|
||||||
|
Picasso.get().load(user.photoUrl).into(binding.photo)
|
||||||
|
}
|
||||||
|
|
||||||
|
is AdminViewModel.State.Error -> binding.error.text = state.errorMessage
|
||||||
|
is AdminViewModel.State.Waiting -> Unit
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.listState.collectWithLifecycle(this) { listState ->
|
||||||
|
adapter.submitData(listState)
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter.loadStateFlow.collectWithLifecycle(this) { data ->
|
||||||
|
val dataState = data.refresh
|
||||||
|
binding.refresh.isRefreshing = dataState is LoadState.Loading
|
||||||
|
binding.error.visibleOrGone(dataState is LoadState.Error)
|
||||||
|
|
||||||
|
if (dataState is LoadState.Error) {
|
||||||
|
binding.error.text = dataState.error.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.refresh.setOnRefreshListener {
|
||||||
|
viewModel.onRefresh()
|
||||||
|
adapter.refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.block.setOnClickListener {
|
||||||
|
AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Блокировка доступа")
|
||||||
|
.setMessage("Вы уверены, что хотите заблокировать доступ работнику: ${binding.fullname}?")
|
||||||
|
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ -> viewModel.onBlock() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
_binding = null
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user