add profile fragment
This commit is contained in:
parent
9ba4ba21db
commit
45a5f9cff5
app/src/main
java/ru/myitschool/work
core
data
auth
user
domain
auth
user
ui
res
@ -1,5 +1,5 @@
|
||||
package ru.myitschool.work.core
|
||||
// БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ
|
||||
object Constants {
|
||||
const val SERVER_ADDRESS = "http://localhost:8090"
|
||||
const val SERVER_ADDRESS = "http://10.0.2.2:8080"
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package ru.myitschool.work.data.auth
|
||||
|
||||
import android.util.Log
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.header
|
||||
@ -17,22 +18,24 @@ import ru.myitschool.work.data.user.UserDto
|
||||
|
||||
object AuthNetworkDataSource {
|
||||
|
||||
suspend fun isUserExist(login: String): Result<Boolean?> = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
val result = client.get("$SERVER_ADDRESS/api/login/$login") //10.0.2.2
|
||||
when (result.status) {
|
||||
HttpStatusCode.OK -> { return@runCatching true }
|
||||
HttpStatusCode.NotFound -> { return@runCatching false }
|
||||
else -> {return@runCatching null }
|
||||
}
|
||||
}
|
||||
}
|
||||
// suspend fun isUserExist(login: String): Result<Boolean?> = withContext(Dispatchers.IO) {
|
||||
// runCatching {
|
||||
// val result = client.get("$SERVER_ADDRESS/api/login/$login") //10.0.2.2
|
||||
// when (result.status) {
|
||||
// HttpStatusCode.OK -> { return@runCatching true }
|
||||
// HttpStatusCode.NotFound -> { return@runCatching false }
|
||||
// else -> {return@runCatching null }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
suspend fun login(token: String): Result<UserDto> = withContext(Dispatchers.IO) {
|
||||
|
||||
runCatching {
|
||||
val result = client.get("$SERVER_ADDRESS/api/login") {
|
||||
header(HttpHeaders.Authorization, token)
|
||||
}
|
||||
Log.d("result", "${result.status}")
|
||||
if (result.status == HttpStatusCode.Unauthorized) {
|
||||
error("Неверный email или пароль")
|
||||
}
|
||||
|
@ -7,9 +7,9 @@ class AuthRepoImpl(
|
||||
private val authNetworkDataSource: AuthNetworkDataSource,
|
||||
private val authStorageDataSource: AuthStorageDataSource
|
||||
) : AuthRepo {
|
||||
override suspend fun isUserExist(email: String): Result<Boolean?> {
|
||||
return authNetworkDataSource.isUserExist(email)
|
||||
}
|
||||
// override suspend fun isUserExist(email: String): Result<Boolean?> {
|
||||
// return authNetworkDataSource.isUserExist(email)
|
||||
// }
|
||||
|
||||
override suspend fun login(email: String, password: String): Result<UserDto> {
|
||||
val token = authStorageDataSource.updateToken(email, password)
|
||||
|
@ -0,0 +1,16 @@
|
||||
package ru.myitschool.work.data.user
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
||||
data class EntranceDto(
|
||||
@SerialName("login")
|
||||
val login : String,
|
||||
@SerialName("name")
|
||||
var name: String,
|
||||
@SerialName("enterAt")
|
||||
var enterAt: String,
|
||||
@SerialName("enterType")
|
||||
var enterType: String,
|
||||
) {
|
||||
|
||||
}
|
@ -18,8 +18,8 @@ data class UserDto(
|
||||
var avatarUrl: String?,
|
||||
@SerialName("position")
|
||||
val position: String,
|
||||
@SerialName("lastEntry")
|
||||
val lastEntry : String,
|
||||
@SerialName("lastEnter")
|
||||
val lastEntry : String? = null,
|
||||
@SerialName("authorities")
|
||||
val authorities : String
|
||||
) {
|
||||
|
@ -0,0 +1,52 @@
|
||||
package ru.sicampus.bootcamp2025.data.user
|
||||
|
||||
import android.util.Log
|
||||
import io.ktor.client.call.body
|
||||
import io.ktor.client.request.get
|
||||
import io.ktor.client.request.header
|
||||
import io.ktor.client.request.put
|
||||
import io.ktor.client.request.setBody
|
||||
import io.ktor.client.statement.bodyAsText
|
||||
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
|
||||
import ru.myitschool.work.data.auth.AuthStorageDataSource.token
|
||||
import ru.myitschool.work.data.auth.Network.client
|
||||
import ru.myitschool.work.data.user.EntranceDto
|
||||
import ru.myitschool.work.data.user.UserDto
|
||||
|
||||
class UserNetworkDataSource {
|
||||
suspend fun getUser(login : String): Result<UserDto> = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
|
||||
val result = client.get("http://$SERVER_ADDRESS/api/${login}/info") {
|
||||
header(HttpHeaders.Authorization, token)
|
||||
}
|
||||
Log.d("tututuut", "${result.status}")
|
||||
|
||||
|
||||
|
||||
if (result.status != HttpStatusCode.OK) {
|
||||
error("Status ${result.status}")
|
||||
}
|
||||
Log.d("result", result.bodyAsText())
|
||||
result.body()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getEntrancesList(login : String) : Result<List<EntranceDto>> = withContext(Dispatchers.IO){
|
||||
runCatching {
|
||||
val result = client.get("http://$SERVER_ADDRESS/api/$login/entrances") {
|
||||
header(HttpHeaders.Authorization, token)
|
||||
}
|
||||
Log.d("serverCode", "${result.status}")
|
||||
if (result.status != HttpStatusCode.OK) {
|
||||
error("Status ${result.status}")
|
||||
}
|
||||
result.body()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package ru.sicampus.bootcamp2025.data.user
|
||||
|
||||
import ru.myitschool.work.data.user.EntranceDto
|
||||
import ru.myitschool.work.data.user.UserDto
|
||||
import ru.myitschool.work.domain.user.EntranceEntity
|
||||
import ru.myitschool.work.domain.user.UserEntity
|
||||
import ru.myitschool.work.domain.user.UserRepo
|
||||
|
||||
|
||||
class UserRepoImpl (
|
||||
private val userNetworkDataSource: UserNetworkDataSource
|
||||
) : UserRepo {
|
||||
override suspend fun getUser(login: String): Result<UserEntity> {
|
||||
return userNetworkDataSource.getUser(login).map { dto ->
|
||||
UserEntity(
|
||||
id = dto.id ?: -1,
|
||||
name = dto.name,
|
||||
avatarUrl = dto.avatarUrl ?: "",
|
||||
authorities = dto.authorities,
|
||||
login = login,
|
||||
position = dto.position,
|
||||
lastEntry = dto.lastEntry ?: "",
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override suspend fun getEntrancesList(login : String): Result<List<EntranceEntity>> {
|
||||
return userNetworkDataSource.getEntrancesList(login).map { userList ->
|
||||
userList.map { it.toEntity() }
|
||||
}
|
||||
}
|
||||
fun EntranceDto.toEntity(): EntranceEntity {
|
||||
return EntranceEntity(
|
||||
login = login,
|
||||
name = this.name,
|
||||
enterAt = this.enterAt,
|
||||
enterType = this.enterType
|
||||
)
|
||||
}
|
||||
}
|
@ -3,6 +3,6 @@ package ru.myitschool.work.domain.auth
|
||||
import ru.myitschool.work.data.user.UserDto
|
||||
|
||||
interface AuthRepo {
|
||||
suspend fun isUserExist(email: String): Result<Boolean?>
|
||||
suspend fun login(email: String, password: String) : Result <UserDto>
|
||||
//suspend fun isUserExist(email: String): Result<Boolean?>
|
||||
suspend fun login(login: String, password: String) : Result <UserDto>
|
||||
}
|
||||
|
@ -3,10 +3,10 @@ package ru.myitschool.work.domain.auth
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
class IsUserExistUseCase (
|
||||
/*class IsUserExistUseCase (
|
||||
private val authRepo : AuthRepo
|
||||
) {
|
||||
suspend operator fun invoke(email : String) : Result<Boolean?> {
|
||||
return authRepo.isUserExist(email)
|
||||
}
|
||||
}
|
||||
}*/
|
@ -5,7 +5,7 @@ import ru.myitschool.work.data.user.UserDto
|
||||
class LoginUseCase(
|
||||
private val authRepo : AuthRepo
|
||||
){
|
||||
suspend operator fun invoke(email : String, password : String) : Result<UserDto> {
|
||||
return authRepo.login(email, password)
|
||||
suspend operator fun invoke(login : String, password : String) : Result<UserDto> {
|
||||
return authRepo.login(login, password)
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package ru.sicampus.bootcamp2025.data
|
||||
|
||||
import io.ktor.client.HttpClient
|
||||
import io.ktor.client.engine.cio.CIO
|
||||
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
|
||||
import io.ktor.serialization.kotlinx.json.json
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
object Network {
|
||||
val client = HttpClient(CIO) {
|
||||
install(ContentNegotiation) {
|
||||
json(Json {
|
||||
isLenient = true
|
||||
ignoreUnknownKeys = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package ru.myitschool.work.domain.user
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
||||
data class EntranceEntity(
|
||||
val login : String,
|
||||
var name: String,
|
||||
var enterAt: String,
|
||||
var enterType: String,
|
||||
) {
|
||||
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package ru.myitschool.work.domain.user
|
||||
|
||||
import ru.myitschool.work.data.auth.AuthStorageDataSource
|
||||
import ru.myitschool.work.data.user.UserDto
|
||||
|
||||
class GetUserUseCase(
|
||||
private val repo: UserRepo,
|
||||
private val authStorageDataSource: AuthStorageDataSource
|
||||
) {
|
||||
suspend fun getUserFromStorage() : UserDto? {
|
||||
return authStorageDataSource.userInfo
|
||||
}
|
||||
suspend operator fun invoke() = repo.getUser(getUserFromStorage()?.login!!)
|
||||
suspend fun getEntrancesList() = repo.getEntrancesList(getUserFromStorage()?.login!!)
|
||||
|
||||
}
|
@ -8,7 +8,7 @@ data class UserEntity(
|
||||
var name: String,
|
||||
var avatarUrl: String?,
|
||||
val position : String,
|
||||
var lastEntry : String,
|
||||
var lastEntry : String?,
|
||||
val authorities : String
|
||||
|
||||
)
|
||||
|
@ -0,0 +1,7 @@
|
||||
package ru.myitschool.work.domain.user
|
||||
|
||||
|
||||
interface UserRepo {
|
||||
suspend fun getUser(login: String) : Result<UserEntity>
|
||||
suspend fun getEntrancesList(login : String) : Result<List<EntranceEntity>>
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package ru.myitschool.work.ui
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@ -17,22 +18,28 @@ import ru.myitschool.work.ui.qr.scan.QrScanFragment
|
||||
// НЕ ИЗМЕНЯЙТЕ НАЗВАНИЕ КЛАССА!
|
||||
@AndroidEntryPoint
|
||||
class RootActivity : AppCompatActivity() {
|
||||
@SuppressLint("ResourceType")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_root)
|
||||
setContentView(R.layout.fragment_profile)
|
||||
val userRole = intent.getStringExtra("USER_ROLE")
|
||||
|
||||
/*val navHostFragment = supportFragmentManager
|
||||
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
|
||||
val navController = navHostFragment.navController*/
|
||||
|
||||
if (navHostFragment != null) {
|
||||
val navController = navHostFragment.navController
|
||||
navController.graph = navController.createGraph(
|
||||
startDestination = LoginDestination
|
||||
) {
|
||||
fragment<LoginFragment, LoginDestination>()
|
||||
fragment<QrScanFragment, QrScanDestination>()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*if (userRole == "ROLE_ADMIN") {
|
||||
bottomNavigationView.menu.clear()
|
||||
bottomNavigationView.inflateMenu(R.menu.bottom_menu_admin)
|
||||
navController.setGraph(R.navigation.main_admin_nav_graph)
|
||||
|
||||
} else {
|
||||
bottomNavigationView.menu.clear()
|
||||
bottomNavigationView.inflateMenu(R.menu.bottom_menu)
|
||||
navController.setGraph(R.navigation.main_nav_graph)
|
||||
}*/
|
||||
|
||||
onBackPressedDispatcher.addCallback(
|
||||
this,
|
||||
@ -44,7 +51,7 @@ class RootActivity : AppCompatActivity() {
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
/*override fun onSupportNavigateUp(): Boolean {
|
||||
val navController = findNavController(R.id.nav_host_fragment)
|
||||
val popBackResult = if (navController.previousBackStackEntry != null) {
|
||||
navController.popBackStack()
|
||||
@ -53,5 +60,5 @@ class RootActivity : AppCompatActivity() {
|
||||
}
|
||||
return popBackResult || super.onSupportNavigateUp()
|
||||
}*/
|
||||
}
|
||||
|
||||
}
|
@ -65,7 +65,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
||||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) = Unit
|
||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) = Unit
|
||||
override fun afterTextChanged(p0: Editable?) {
|
||||
|
||||
viewModel.changeLogin()
|
||||
}
|
||||
|
||||
})
|
||||
|
@ -17,14 +17,13 @@ import ru.myitschool.work.R
|
||||
import ru.myitschool.work.data.auth.AuthNetworkDataSource
|
||||
import ru.myitschool.work.data.auth.AuthRepoImpl
|
||||
import ru.myitschool.work.data.auth.AuthStorageDataSource
|
||||
import ru.myitschool.work.domain.auth.IsUserExistUseCase
|
||||
//import ru.myitschool.work.domain.auth.IsUserExistUseCase
|
||||
import ru.myitschool.work.domain.auth.LoginUseCase
|
||||
import javax.inject.Inject
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
class LoginViewModel constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val isUserExistUseCase: IsUserExistUseCase,
|
||||
private val loginUseCase: LoginUseCase
|
||||
) : ViewModel() {
|
||||
private val _state = MutableStateFlow<State>(getStateShow())
|
||||
@ -49,44 +48,23 @@ class LoginViewModel constructor(
|
||||
)
|
||||
{
|
||||
viewModelScope.launch {
|
||||
when (checkUserExistence(login)) {
|
||||
true -> {
|
||||
loginUser(login, password)
|
||||
}
|
||||
false -> {
|
||||
updateState(context.getString(R.string.error_invalid_credentials))
|
||||
}
|
||||
null -> updateState(context.getString(R.string.error_unknown))
|
||||
}
|
||||
loginUser(login, password)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkUserExistence(email: String):Boolean?{
|
||||
return try {
|
||||
val result = isUserExistUseCase(email)
|
||||
result.fold(
|
||||
onSuccess = {isExist -> isExist},
|
||||
onFailure = {
|
||||
Log.e("LoginViewModel", "Error checking user existence", it)
|
||||
null
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e("LoginViewModel", "Error during user existence check", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private suspend fun loginUser(email: String, password: String) {
|
||||
loginUseCase(email, password).fold(
|
||||
onSuccess = { user ->
|
||||
println("Login successful")
|
||||
Log.d("loginViewModel","Login successful")
|
||||
_userRole.emit(user.authorities)
|
||||
_navigateToMain.emit(user.authorities)
|
||||
},
|
||||
onFailure = { error ->
|
||||
updateState(error.message ?: context.getString(R.string.error_unknown))
|
||||
Log.d("errorLoginViewModel","${error.message}")
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -125,7 +103,6 @@ class LoginViewModel constructor(
|
||||
)
|
||||
return LoginViewModel(
|
||||
application,
|
||||
isUserExistUseCase = IsUserExistUseCase(authRepoImpl),
|
||||
loginUseCase = LoginUseCase(authRepoImpl)
|
||||
) as T
|
||||
}
|
||||
|
@ -0,0 +1,68 @@
|
||||
package ru.myitschool.work.ui.profile
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
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.FragmentProfileBinding
|
||||
import ru.myitschool.work.ui.login.EntryActivity
|
||||
import ru.myitschool.work.utils.collectWithLifecycle
|
||||
|
||||
class ProfileFragment : Fragment(R.layout.fragment_profile) {
|
||||
private var _viewBinding: FragmentProfileBinding? = null
|
||||
private val viewBinding: FragmentProfileBinding get() = _viewBinding!!
|
||||
|
||||
private val viewModel by viewModels<ProfileViewModel> { ProfileViewModel.Factory }
|
||||
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
_viewBinding = FragmentProfileBinding.bind(view)
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
|
||||
viewBinding.refresh.setOnClickListener { viewModel.clickRefresh() }
|
||||
viewBinding.refreshForProfile.setOnClickListener { viewModel.clickRefresh() }
|
||||
viewBinding.logout.setOnClickListener {
|
||||
val intent = Intent(requireContext(), EntryActivity::class.java)
|
||||
startActivity(intent)
|
||||
requireActivity().finish()
|
||||
}
|
||||
|
||||
|
||||
viewModel.state.collectWithLifecycle(this) { state ->
|
||||
|
||||
viewBinding.error.visibility = if (state is ProfileViewModel.State.Error) View.VISIBLE else View.GONE
|
||||
viewBinding.loading.visibility = if (state is ProfileViewModel.State.Loading) View.VISIBLE else View.GONE
|
||||
viewBinding.profile.visibility = if (state is ProfileViewModel.State.Show) View.VISIBLE else View.GONE
|
||||
|
||||
when(state) {
|
||||
is ProfileViewModel.State.Loading -> Unit
|
||||
is ProfileViewModel.State.Show -> {
|
||||
viewBinding.name.text = state.profileInfo.name
|
||||
viewBinding.position.text = "Должность: ${state.profileInfo.name}"
|
||||
if (state.profileInfo.lastEntry == null) viewBinding.lastEntry.text = "Время последнего входа: Нет данных"
|
||||
else viewBinding.lastEntry.text = "Время последнего входа: ${state.profileInfo.lastEntry}"
|
||||
Picasso.get().load(state.profileInfo.avatarUrl).resize(100, 100).centerCrop().into(viewBinding.imageView)
|
||||
|
||||
//if (state.entrancesList == emptyList())
|
||||
//viewBinding.recyclerView.visibility = View.GONE
|
||||
}
|
||||
is ProfileViewModel.State.Error -> {
|
||||
viewBinding.errorText.text = state.text
|
||||
//viewBinding.noData.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroyView() {
|
||||
_viewBinding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package ru.myitschool.work.ui.profile
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import ru.myitschool.work.data.auth.AuthStorageDataSource
|
||||
import ru.myitschool.work.domain.user.EntranceEntity
|
||||
import ru.myitschool.work.domain.user.GetUserUseCase
|
||||
import ru.myitschool.work.domain.user.UserEntity
|
||||
import ru.sicampus.bootcamp2025.data.user.UserNetworkDataSource
|
||||
import ru.sicampus.bootcamp2025.data.user.UserRepoImpl
|
||||
|
||||
class ProfileViewModel(
|
||||
private val getUserUseCase: GetUserUseCase
|
||||
) : ViewModel() {
|
||||
private val _state = MutableStateFlow<State>(State.Loading)
|
||||
val state = _state.asStateFlow()
|
||||
|
||||
init {
|
||||
updateStateGet()
|
||||
}
|
||||
|
||||
fun clickRefresh() {
|
||||
updateStateGet()
|
||||
}
|
||||
|
||||
fun updateStateGet() {
|
||||
viewModelScope.launch {
|
||||
_state.emit(State.Loading)
|
||||
val entranceList : List<EntranceEntity> = getUserUseCase.getEntrancesList().fold(
|
||||
onSuccess = { list ->
|
||||
list
|
||||
},
|
||||
onFailure = {
|
||||
emptyList()
|
||||
}
|
||||
)
|
||||
_state.emit(
|
||||
getUserUseCase.invoke().fold(
|
||||
onSuccess = { data ->
|
||||
Log.d("uraa", "успех успех ${data.toString()}")
|
||||
State.Show(data, entranceList)
|
||||
},
|
||||
onFailure = { error ->
|
||||
Log.d("kaput", error.message.toString())
|
||||
State.Error(error.message.toString())
|
||||
}
|
||||
)
|
||||
)
|
||||
//_state.emit(State.Error("о нет ошибка ошибка помогите"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
sealed interface State {
|
||||
data object Loading: State
|
||||
data class Show(
|
||||
val profileInfo: UserEntity,
|
||||
val entrancesList : List<EntranceEntity>
|
||||
) : State
|
||||
data class Error(
|
||||
val text: String
|
||||
) : State
|
||||
}
|
||||
companion object {
|
||||
val Factory : ViewModelProvider.Factory = object : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ProfileViewModel(
|
||||
getUserUseCase = GetUserUseCase(
|
||||
repo = UserRepoImpl(
|
||||
userNetworkDataSource = UserNetworkDataSource()
|
||||
),
|
||||
authStorageDataSource = AuthStorageDataSource
|
||||
)
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -106,7 +106,7 @@
|
||||
tools:text="Иванова Вера Павловна" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:id="@+id/position"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
@ -118,6 +118,7 @@
|
||||
tools:text="Должность: Разработчик" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/lastEntry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="24dp"
|
||||
@ -125,7 +126,7 @@
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/position"
|
||||
tools:text="Время последнего входа: 12:00 16.09" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
@ -167,6 +168,7 @@
|
||||
|
||||
|
||||
<View
|
||||
android:id="@+id/view"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_marginTop="7dp"
|
||||
@ -189,25 +191,70 @@
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:elevation="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/entry_time"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/view2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/view3"
|
||||
app:layout_constraintHorizontal_bias="0.51"
|
||||
app:layout_constraintStart_toStartOf="@+id/view2"
|
||||
app:layout_constraintTop_toTopOf="@+id/view2"
|
||||
app:layout_constraintVertical_bias="0.0" />
|
||||
app:layout_constraintTop_toTopOf="@+id/view2" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:elevation="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/entry"
|
||||
android:textColor="@color/white"
|
||||
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/view"
|
||||
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/view3"
|
||||
app:layout_constraintTop_toTopOf="@+id/view2" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:elevation="10dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/type_of_entrance"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="14sp"
|
||||
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/view"
|
||||
app:layout_constraintTop_toTopOf="@+id/view2" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/no_data"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="28dp"
|
||||
android:text="@string/no_data"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.498"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/view2" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
|
17
app/src/main/res/navigation/main_admin_nav_graph.xml
Normal file
17
app/src/main/res/navigation/main_admin_nav_graph.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/admin_main_nav_graph">
|
||||
|
||||
<!--<fragment
|
||||
android:id="@+id/fragment_profile"
|
||||
android:name=""
|
||||
android:label="Profile"
|
||||
tools:layout="@layout/fragment_profile" />
|
||||
<fragment
|
||||
android:id="@+id/fragment_user_list"
|
||||
android:name="ru.sicampus.bootcamp2025.ui.userList.FreeVolunteersListFragment"
|
||||
android:label="UserList"
|
||||
tools:layout="@layout/fragment_free_volunteers_list" />-->
|
||||
|
||||
</navigation>
|
@ -10,4 +10,7 @@
|
||||
<string name="error_unknown">Непредвиденная ошибка</string>
|
||||
<string name="entry_history">История входов\n</string>
|
||||
<string name="entry_time">Время входа\n</string>
|
||||
<string name="entry">Вход</string>
|
||||
<string name="type_of_entrance">Тип прохода</string>
|
||||
<string name="no_data">Нет данных\n</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user