Merge branch 'Frontend' into Frontend_UI
# Conflicts: # app/src/main/AndroidManifest.xml # app/src/main/java/ru/myitschool/work/ui/Main/AdminFragment.kt # app/src/main/res/layout/fragment_main.xml
This commit is contained in:
		
						commit
						ee24f241db
					
				@ -47,6 +47,10 @@ dependencies {
 | 
				
			|||||||
    implementation("androidx.constraintlayout:constraintlayout:2.1.4")
 | 
					    implementation("androidx.constraintlayout:constraintlayout:2.1.4")
 | 
				
			||||||
    kapt ("com.github.bumptech.glide:compiler:4.15.1")
 | 
					    kapt ("com.github.bumptech.glide:compiler:4.15.1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
 | 
				
			||||||
 | 
					    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
 | 
				
			||||||
 | 
					    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    defaultLibrary()
 | 
					    defaultLibrary()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    implementation(Dependencies.AndroidX.activity)
 | 
					    implementation(Dependencies.AndroidX.activity)
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,8 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
    xmlns:tools="http://schemas.android.com/tools"> <!-- Минимальная и целевая версия SDK по тз (28 и 34) -->
 | 
					    xmlns:tools="http://schemas.android.com/tools">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Минимальная и целевая версия SDK по тз (28 и 34) -->
 | 
				
			||||||
    <uses-sdk
 | 
					    <uses-sdk
 | 
				
			||||||
        android:minSdkVersion="28"
 | 
					        android:minSdkVersion="28"
 | 
				
			||||||
        android:targetSdkVersion="34" />
 | 
					        android:targetSdkVersion="34" />
 | 
				
			||||||
@ -22,9 +24,6 @@
 | 
				
			|||||||
        android:supportsRtl="true"
 | 
					        android:supportsRtl="true"
 | 
				
			||||||
        android:theme="@style/Theme.Default"
 | 
					        android:theme="@style/Theme.Default"
 | 
				
			||||||
        tools:targetApi="34">
 | 
					        tools:targetApi="34">
 | 
				
			||||||
        <activity
 | 
					 | 
				
			||||||
            android:name=".ui.Main.AdminFragment"
 | 
					 | 
				
			||||||
            android:exported="false" />
 | 
					 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
            android:name=".ui.RootActivity"
 | 
					            android:name=".ui.RootActivity"
 | 
				
			||||||
            android:exported="true">
 | 
					            android:exported="true">
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,12 @@
 | 
				
			|||||||
package ru.myitschool.work
 | 
					package ru.myitschool.work
 | 
				
			||||||
 | 
					
 | 
				
			||||||
object SessionManager {
 | 
					object SessionManager {
 | 
				
			||||||
    var userLogin: String = ""
 | 
					    var userLogin: String? = null // Логин пользователя
 | 
				
			||||||
 | 
					    var userRole: String? = null // Роль пользователя
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Метод для очистки данных сессии
 | 
				
			||||||
 | 
					    fun clearSession() {
 | 
				
			||||||
 | 
					        userLogin = null
 | 
				
			||||||
 | 
					        userRole = null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -4,9 +4,9 @@ import dagger.Module
 | 
				
			|||||||
import dagger.Provides
 | 
					import dagger.Provides
 | 
				
			||||||
import dagger.hilt.InstallIn
 | 
					import dagger.hilt.InstallIn
 | 
				
			||||||
import dagger.hilt.components.SingletonComponent
 | 
					import dagger.hilt.components.SingletonComponent
 | 
				
			||||||
 | 
					import okhttp3.OkHttpClient
 | 
				
			||||||
import retrofit2.Retrofit
 | 
					import retrofit2.Retrofit
 | 
				
			||||||
import retrofit2.converter.gson.GsonConverterFactory
 | 
					import retrofit2.converter.gson.GsonConverterFactory
 | 
				
			||||||
import ru.myitschool.work.api.ApiService
 | 
					 | 
				
			||||||
import ru.myitschool.work.core.Constants
 | 
					import ru.myitschool.work.core.Constants
 | 
				
			||||||
import javax.inject.Singleton
 | 
					import javax.inject.Singleton
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -16,9 +16,20 @@ object ApiModule {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Provides
 | 
					    @Provides
 | 
				
			||||||
    @Singleton
 | 
					    @Singleton
 | 
				
			||||||
    fun provideRetrofit(): Retrofit {
 | 
					    fun provideOkHttpClient(): OkHttpClient {
 | 
				
			||||||
 | 
					        val username = "pivanov" // Замените на ваш логин
 | 
				
			||||||
 | 
					        val password = "password123" // Замените на ваш пароль
 | 
				
			||||||
 | 
					        return OkHttpClient.Builder()
 | 
				
			||||||
 | 
					            .addInterceptor(AuthInterceptor(username, password))
 | 
				
			||||||
 | 
					            .build()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Provides
 | 
				
			||||||
 | 
					    @Singleton
 | 
				
			||||||
 | 
					    fun provideRetrofit(client: OkHttpClient): Retrofit {
 | 
				
			||||||
        return Retrofit.Builder()
 | 
					        return Retrofit.Builder()
 | 
				
			||||||
            .baseUrl(Constants.SERVER_ADDRESS)
 | 
					            .baseUrl(Constants.SERVER_ADDRESS)
 | 
				
			||||||
 | 
					            .client(client)
 | 
				
			||||||
            .addConverterFactory(GsonConverterFactory.create())
 | 
					            .addConverterFactory(GsonConverterFactory.create())
 | 
				
			||||||
            .build()
 | 
					            .build()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -3,22 +3,56 @@ package ru.myitschool.work.api
 | 
				
			|||||||
import retrofit2.Response
 | 
					import retrofit2.Response
 | 
				
			||||||
import retrofit2.http.Body
 | 
					import retrofit2.http.Body
 | 
				
			||||||
import retrofit2.http.GET
 | 
					import retrofit2.http.GET
 | 
				
			||||||
import retrofit2.http.Header
 | 
					 | 
				
			||||||
import retrofit2.http.PATCH
 | 
					import retrofit2.http.PATCH
 | 
				
			||||||
 | 
					import retrofit2.http.POST
 | 
				
			||||||
import retrofit2.http.Path
 | 
					import retrofit2.http.Path
 | 
				
			||||||
 | 
					import retrofit2.http.Query
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface ApiService {
 | 
					interface ApiService {
 | 
				
			||||||
    @GET("api/{login}/auth")
 | 
					    // Метод для аутентификации
 | 
				
			||||||
 | 
					    @GET("/api/auth") // Используем GET для аутентификации
 | 
				
			||||||
    suspend fun authenticate(
 | 
					    suspend fun authenticate(
 | 
				
			||||||
        @Path("login") login: String,
 | 
					        @Query("login") login: String, // Передаем логин как параметр запроса
 | 
				
			||||||
        @Header("Authorization") authorization: String // Добавляем заголовок Authorization
 | 
					        @Query("password") password: String // Передаем пароль как параметр запроса
 | 
				
			||||||
    ): Response<Unit>
 | 
					    ): Response<UserAuthResponse> // Возвращаем JSON как объект UserAuthResponse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @GET("api/{login}/info")
 | 
					    // Другие методы...
 | 
				
			||||||
    suspend fun getUserInfo(@Path("login") login: String): Response<Map<String, Any>> // Возвращаем Map вместо UserInfo
 | 
					    @GET("/api/{login}/info") // Получение информации о пользователе
 | 
				
			||||||
 | 
					    suspend fun getUserInfo(@Path("login") login: String): Response<Map<String, Any>>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @PATCH("api/{login}/open")
 | 
					    @GET("/api/employee/{login}") // Получение информации о сотруднике
 | 
				
			||||||
    suspend fun openDoor(@Path("login") login: String, @Body body: OpenDoorRequest): Response<Unit>
 | 
					    suspend fun getEmployeeInfo(@Path("login") login: String): Response<EmployeeData>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @PATCH("/api/open") // Открыть дверь
 | 
				
			||||||
 | 
					    suspend fun openDoor(@Body request: OpenDoorRequest): Response<String>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @POST("/api/employee/toggleAccess") // Метод для блокировки/разблокировки доступа
 | 
				
			||||||
 | 
					    suspend fun toggleAccess(@Body request: ToggleAccessRequest): Response<Unit>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @GET("/api/workers") // Получить всех сотрудников
 | 
				
			||||||
 | 
					    suspend fun getAllWorkers(): Response<List<EmployeeData>>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
data class OpenDoorRequest(val value: String)
 | 
					// Модель данных для ответа аутентификации
 | 
				
			||||||
 | 
					data class UserAuthResponse(
 | 
				
			||||||
 | 
					    val role: String // Добавляем поле для роли
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Модель данных для информации о сотруднике
 | 
				
			||||||
 | 
					data class EmployeeData(
 | 
				
			||||||
 | 
					    val name: String,
 | 
				
			||||||
 | 
					    val position: String,
 | 
				
			||||||
 | 
					    val lastVisit: String
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Модель данных для запроса блокировки/разблокировки доступа
 | 
				
			||||||
 | 
					data class ToggleAccessRequest(
 | 
				
			||||||
 | 
					    val login: String, // Логин сотрудника
 | 
				
			||||||
 | 
					    val action: String // Действие: "block" или "unblock"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Модель данных для запроса открытия двери
 | 
				
			||||||
 | 
					data class OpenDoorRequest(
 | 
				
			||||||
 | 
					    val login: String, // Логин сотрудника
 | 
				
			||||||
 | 
					    val value: Long // Код для открытия двери
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										16
									
								
								app/src/main/java/ru/myitschool/work/api/AuthInterceptor.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/src/main/java/ru/myitschool/work/api/AuthInterceptor.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					package ru.myitschool.work.api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import okhttp3.Interceptor
 | 
				
			||||||
 | 
					import okhttp3.Response
 | 
				
			||||||
 | 
					import java.util.Base64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AuthInterceptor(private val username: String, private val password: String) : Interceptor {
 | 
				
			||||||
 | 
					    override fun intercept(chain: Interceptor.Chain): Response {
 | 
				
			||||||
 | 
					        val originalRequest = chain.request()
 | 
				
			||||||
 | 
					        val credential = Base64.getEncoder().encodeToString("$username:$password".toByteArray())
 | 
				
			||||||
 | 
					        val newRequest = originalRequest.newBuilder()
 | 
				
			||||||
 | 
					            .addHeader("Authorization", "Basic $credential")
 | 
				
			||||||
 | 
					            .build()
 | 
				
			||||||
 | 
					        return chain.proceed(newRequest)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,5 +1,5 @@
 | 
				
			|||||||
package ru.myitschool.work.core
 | 
					package ru.myitschool.work.core
 | 
				
			||||||
// БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ
 | 
					// БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ
 | 
				
			||||||
object Constants {
 | 
					object Constants {
 | 
				
			||||||
    const val SERVER_ADDRESS = "const val SERVER_ADDRESS = \"http://localhost:8090\"\n"
 | 
					    const val SERVER_ADDRESS = "http://192.168.1.131:8080"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,21 +1,108 @@
 | 
				
			|||||||
package ru.myitschool.work.ui.Main
 | 
					package ru.myitschool.work.ui.Main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.os.Bundle
 | 
					import android.os.Bundle
 | 
				
			||||||
import androidx.activity.enableEdgeToEdge
 | 
					import android.view.View
 | 
				
			||||||
import androidx.appcompat.app.AppCompatActivity
 | 
					import android.widget.Toast
 | 
				
			||||||
import androidx.core.view.ViewCompat
 | 
					import androidx.fragment.app.Fragment
 | 
				
			||||||
import androidx.core.view.WindowInsetsCompat
 | 
					import androidx.lifecycle.lifecycleScope
 | 
				
			||||||
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
 | 
					import retrofit2.Retrofit
 | 
				
			||||||
 | 
					import retrofit2.converter.gson.GsonConverterFactory
 | 
				
			||||||
import ru.myitschool.work.R
 | 
					import ru.myitschool.work.R
 | 
				
			||||||
 | 
					import ru.myitschool.work.api.ApiService
 | 
				
			||||||
 | 
					import ru.myitschool.work.api.EmployeeData
 | 
				
			||||||
 | 
					import ru.myitschool.work.api.ToggleAccessRequest
 | 
				
			||||||
 | 
					import ru.myitschool.work.core.Constants
 | 
				
			||||||
 | 
					import ru.myitschool.work.databinding.FragmentAdminBinding
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AdminFragment : AppCompatActivity() {
 | 
					class AdminFragment : Fragment(R.layout.fragment_admin) {
 | 
				
			||||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
					    private var _binding: FragmentAdminBinding? = null
 | 
				
			||||||
        super.onCreate(savedInstanceState)
 | 
					    private val binding get() = _binding!!
 | 
				
			||||||
        enableEdgeToEdge()
 | 
					
 | 
				
			||||||
        setContentView(R.layout.fragment_admin)
 | 
					    private val apiService: ApiService by lazy {
 | 
				
			||||||
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
 | 
					        Retrofit.Builder()
 | 
				
			||||||
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
 | 
					            .baseUrl(Constants.SERVER_ADDRESS)
 | 
				
			||||||
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
 | 
					            .addConverterFactory(GsonConverterFactory.create())
 | 
				
			||||||
            insets
 | 
					            .build()
 | 
				
			||||||
 | 
					            .create(ApiService::class.java)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var isAccessBlocked: Boolean = false // Переменная для отслеживания состояния доступа
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
				
			||||||
 | 
					        super.onViewCreated(view, savedInstanceState)
 | 
				
			||||||
 | 
					        _binding = FragmentAdminBinding.bind(view)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setupUI()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun setupUI() {
 | 
				
			||||||
 | 
					        binding.viewEmployeeInfo.setOnClickListener {
 | 
				
			||||||
 | 
					            val login = binding.employeeLogin.text.toString()
 | 
				
			||||||
 | 
					            if (login.isNotEmpty()) {
 | 
				
			||||||
 | 
					                fetchEmployeeInfo(login)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Toast.makeText(requireContext(), "Введите логин сотрудника", Toast.LENGTH_SHORT).show()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        binding.toggleAccess.setOnClickListener {
 | 
				
			||||||
 | 
					            val login = binding.employeeLogin.text.toString()
 | 
				
			||||||
 | 
					            if (login.isNotEmpty()) {
 | 
				
			||||||
 | 
					                // Определяем действие на основе текущего состояния доступа
 | 
				
			||||||
 | 
					                val action = if (isAccessBlocked) "unblock" else "block"
 | 
				
			||||||
 | 
					                toggleEmployeeAccess(login, action)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Toast.makeText(requireContext(), "Введите логин сотрудника", Toast.LENGTH_SHORT).show()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun fetchEmployeeInfo(login: String) {
 | 
				
			||||||
 | 
					        lifecycleScope.launch {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                val response = apiService.getEmployeeInfo(login)
 | 
				
			||||||
 | 
					                if (response.isSuccessful) {
 | 
				
			||||||
 | 
					                    val employeeData = response.body()
 | 
				
			||||||
 | 
					                    employeeData?.let {
 | 
				
			||||||
 | 
					                        binding.employeeInfo.text = "Имя: ${it.name}, Должность: ${it.position}, Последний визит: ${it.lastVisit}"
 | 
				
			||||||
 | 
					                        binding.employeeInfo.visibility = View.VISIBLE
 | 
				
			||||||
 | 
					                        binding.toggleAccess.visibility = View.VISIBLE
 | 
				
			||||||
 | 
					                        // Здесь можно установить состояние доступа, если оно доступно
 | 
				
			||||||
 | 
					                        isAccessBlocked = false // Предположим, что доступ не заблокирован по умолчанию
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Toast.makeText(requireContext(), "Ошибка получения данных", Toast.LENGTH_SHORT).show()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (e: Exception) {
 | 
				
			||||||
 | 
					                e.printStackTrace()
 | 
				
			||||||
 | 
					                Toast.makeText(requireContext(), "Ошибка сети", Toast.LENGTH_SHORT).show()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun toggleEmployeeAccess(login: String, action: String) {
 | 
				
			||||||
 | 
					        val request = ToggleAccessRequest(login, action)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lifecycleScope.launch {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                val response = apiService.toggleAccess(request)
 | 
				
			||||||
 | 
					                if (response.isSuccessful) {
 | 
				
			||||||
 | 
					                    isAccessBlocked = !isAccessBlocked // Переключаем состояние доступа
 | 
				
			||||||
 | 
					                    val message = if (action == "block") "Доступ заблокирован" else "Доступ разблокирован"
 | 
				
			||||||
 | 
					                    Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Toast.makeText(requireContext(), "Ошибка изменения доступа", Toast.LENGTH_SHORT).show()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (e: Exception) {
 | 
				
			||||||
 | 
					                e.printStackTrace()
 | 
				
			||||||
 | 
					                Toast.makeText(requireContext(), "Ошибка сети", Toast.LENGTH_SHORT).show()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onDestroyView() {
 | 
				
			||||||
 | 
					        _binding = null // Освобождаем binding, когда представление уничтожается
 | 
				
			||||||
 | 
					        super.onDestroyView()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -10,7 +10,6 @@ import androidx.fragment.app.setFragmentResultListener
 | 
				
			|||||||
import androidx.lifecycle.lifecycleScope
 | 
					import androidx.lifecycle.lifecycleScope
 | 
				
			||||||
import androidx.navigation.fragment.findNavController
 | 
					import androidx.navigation.fragment.findNavController
 | 
				
			||||||
import androidx.recyclerview.widget.LinearLayoutManager
 | 
					import androidx.recyclerview.widget.LinearLayoutManager
 | 
				
			||||||
import androidx.recyclerview.widget.RecyclerView
 | 
					 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
import kotlinx.coroutines.withContext
 | 
					import kotlinx.coroutines.withContext
 | 
				
			||||||
@ -47,11 +46,24 @@ class MainFragment : Fragment(R.layout.fragment_main) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        setupUI()
 | 
					        setupUI()
 | 
				
			||||||
        fetchUserData()
 | 
					        fetchUserData()
 | 
				
			||||||
 | 
					        checkAdminAccess() // Проверяем доступ администратора
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Проверяем, есть ли результат QR
 | 
					        // Проверяем, есть ли результат QR
 | 
				
			||||||
        checkQrResult()
 | 
					        checkQrResult()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun checkAdminAccess() {
 | 
				
			||||||
 | 
					        // Проверяем, является ли пользователь администратором
 | 
				
			||||||
 | 
					        if (SessionManager.userRole == "admin") {
 | 
				
			||||||
 | 
					            binding.adminPanel.visibility = View.VISIBLE // Показываем кнопку AdminPanel
 | 
				
			||||||
 | 
					            binding.adminPanel.setOnClickListener {
 | 
				
			||||||
 | 
					                findNavController().navigate(R.id.adminFragment) // Переход на экран администратора
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            binding.adminPanel.visibility = View.GONE // Скрываем кнопку для обычных пользователей
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun checkQrResult() {
 | 
					    private fun checkQrResult() {
 | 
				
			||||||
        // Слушаем результат QR сканирования
 | 
					        // Слушаем результат QR сканирования
 | 
				
			||||||
        setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle ->
 | 
					        setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle ->
 | 
				
			||||||
@ -81,7 +93,9 @@ class MainFragment : Fragment(R.layout.fragment_main) {
 | 
				
			|||||||
        lifecycleScope.launch {
 | 
					        lifecycleScope.launch {
 | 
				
			||||||
            showError(null) // Скрыть ошибку, если она была
 | 
					            showError(null) // Скрыть ошибку, если она была
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                val response = apiService.getUserInfo(SessionManager.userLogin) // Получаем данные пользователя
 | 
					                val response =
 | 
				
			||||||
 | 
					                    SessionManager.userLogin?.let { apiService.getUserInfo(it) } // Получаем данные пользователя
 | 
				
			||||||
 | 
					                if (response != null) {
 | 
				
			||||||
                    if (response.isSuccessful) {
 | 
					                    if (response.isSuccessful) {
 | 
				
			||||||
                        response.body()?.let { data ->
 | 
					                        response.body()?.let { data ->
 | 
				
			||||||
                            // Извлекаем значения из Map
 | 
					                            // Извлекаем значения из Map
 | 
				
			||||||
@ -101,6 +115,7 @@ class MainFragment : Fragment(R.layout.fragment_main) {
 | 
				
			|||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        showError(getString(R.string.error_loading_data)) // Показываем ошибку, если данные не загрузились
 | 
					                        showError(getString(R.string.error_loading_data)) // Показываем ошибку, если данные не загрузились
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            } catch (e: Exception) {
 | 
					            } catch (e: Exception) {
 | 
				
			||||||
                showError(e.localizedMessage) // Показываем ошибку при исключении
 | 
					                showError(e.localizedMessage) // Показываем ошибку при исключении
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -10,15 +10,17 @@ import android.widget.Toast
 | 
				
			|||||||
import androidx.appcompat.app.AlertDialog
 | 
					import androidx.appcompat.app.AlertDialog
 | 
				
			||||||
import androidx.fragment.app.Fragment
 | 
					import androidx.fragment.app.Fragment
 | 
				
			||||||
import androidx.fragment.app.viewModels
 | 
					import androidx.fragment.app.viewModels
 | 
				
			||||||
 | 
					import androidx.lifecycle.lifecycleScope
 | 
				
			||||||
import androidx.navigation.fragment.findNavController
 | 
					import androidx.navigation.fragment.findNavController
 | 
				
			||||||
import dagger.hilt.android.AndroidEntryPoint
 | 
					import dagger.hilt.android.AndroidEntryPoint
 | 
				
			||||||
import ru.myitschool.work.R
 | 
					import ru.myitschool.work.R
 | 
				
			||||||
 | 
					import ru.myitschool.work.SessionManager
 | 
				
			||||||
 | 
					import ru.myitschool.work.api.UserAuthResponse
 | 
				
			||||||
import ru.myitschool.work.databinding.FragmentLoginBinding
 | 
					import ru.myitschool.work.databinding.FragmentLoginBinding
 | 
				
			||||||
import ru.myitschool.work.utils.collectWhenStarted
 | 
					import ru.myitschool.work.utils.collectWhenStarted
 | 
				
			||||||
import ru.myitschool.work.utils.visibleOrGone
 | 
					import ru.myitschool.work.utils.visibleOrGone
 | 
				
			||||||
import ru.myitschool.work.utils.AuthPreferences
 | 
					import ru.myitschool.work.utils.AuthPreferences
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
import androidx.lifecycle.lifecycleScope
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@AndroidEntryPoint
 | 
					@AndroidEntryPoint
 | 
				
			||||||
class LoginFragment : Fragment(R.layout.fragment_login) {
 | 
					class LoginFragment : Fragment(R.layout.fragment_login) {
 | 
				
			||||||
@ -57,6 +59,7 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
 | 
				
			|||||||
            performLogin(username, password) // Передаем пароль в метод performLogin
 | 
					            performLogin(username, password) // Передаем пароль в метод performLogin
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Изначально скрываем индикаторы
 | 
				
			||||||
        binding.loading.visibleOrGone(false)
 | 
					        binding.loading.visibleOrGone(false)
 | 
				
			||||||
        binding.error.visibleOrGone(false)
 | 
					        binding.error.visibleOrGone(false)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -76,30 +79,35 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private fun performLogin(username: String, password: String) {
 | 
					    private fun performLogin(username: String, password: String) {
 | 
				
			||||||
        lifecycleScope.launch {
 | 
					        lifecycleScope.launch {
 | 
				
			||||||
 | 
					            binding.loading.visibleOrGone(true) // Показываем индикатор загрузки
 | 
				
			||||||
            viewModel.authenticate(username, password) // Передаем пароль в метод authenticate
 | 
					            viewModel.authenticate(username, password) // Передаем пароль в метод authenticate
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun subscribe() {
 | 
					    private fun subscribe() {
 | 
				
			||||||
        viewModel.state.collectWhenStarted(this) { state ->
 | 
					        lifecycleScope.launch {
 | 
				
			||||||
 | 
					            viewModel.state.collect { state ->
 | 
				
			||||||
                binding.loading.visibleOrGone(false)
 | 
					                binding.loading.visibleOrGone(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                if (state.maintenance) {
 | 
					                if (state.maintenance) {
 | 
				
			||||||
                    showMaintenanceDialog() // Показываем диалог о техработах
 | 
					                    showMaintenanceDialog() // Показываем диалог о техработах
 | 
				
			||||||
                } else if (state.error != null) {
 | 
					 | 
				
			||||||
                    binding.error.text = state.error
 | 
					 | 
				
			||||||
                    binding.error.visibility = View.VISIBLE
 | 
					 | 
				
			||||||
                } else if (state.success) {
 | 
					                } else if (state.success) {
 | 
				
			||||||
                    binding.error.visibility = View.GONE
 | 
					                    binding.error.visibility = View.GONE
 | 
				
			||||||
                    authPreferences.saveLoginState(true)
 | 
					                    authPreferences.saveLoginState(true)
 | 
				
			||||||
                    authPreferences.saveLogin(binding.username.text.toString()) // Сохраняем логин
 | 
					                    authPreferences.saveLogin(binding.username.text.toString()) // Сохраняем логин
 | 
				
			||||||
                    Toast.makeText(context, "Авторизация прошла успешно", Toast.LENGTH_SHORT).show()
 | 
					
 | 
				
			||||||
                    navigateToMainScreen()
 | 
					                    // Сохраняем роль пользователя в SessionManager
 | 
				
			||||||
 | 
					                    val userAuthResponse: UserAuthResponse? = state.userAuthResponse
 | 
				
			||||||
 | 
					                    userAuthResponse?.let {
 | 
				
			||||||
 | 
					                        SessionManager.userRole = it.role // Сохраняем роль
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    Toast.makeText(context, "Авторизация прошла успешно", Toast.LENGTH_SHORT).show()
 | 
				
			||||||
 | 
					                    navigateToMainScreen() // Перенаправление на следующий экран
 | 
				
			||||||
 | 
					                } else if (state.error != null) {
 | 
				
			||||||
 | 
					                    binding.error.text = state.error
 | 
				
			||||||
 | 
					                    binding.error.visibility = View.VISIBLE
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } catch (e: Exception) {
 | 
					 | 
				
			||||||
                Log.e("LoginFragment", "Ошибка при обработке состояния", e)
 | 
					 | 
				
			||||||
                Toast.makeText(context, "Произошла ошибка. Пожалуйста, попробуйте снова.", Toast.LENGTH_SHORT).show()
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,52 +1,48 @@
 | 
				
			|||||||
package ru.myitschool.work.ui.login
 | 
					package ru.myitschool.work.ui.login
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.content.Context
 | 
					import android.util.Log
 | 
				
			||||||
import androidx.lifecycle.ViewModel
 | 
					import androidx.lifecycle.ViewModel
 | 
				
			||||||
import androidx.lifecycle.viewModelScope
 | 
					import androidx.lifecycle.viewModelScope
 | 
				
			||||||
import dagger.hilt.android.lifecycle.HiltViewModel
 | 
					import dagger.hilt.android.lifecycle.HiltViewModel
 | 
				
			||||||
import dagger.hilt.android.qualifiers.ApplicationContext
 | 
					 | 
				
			||||||
import kotlinx.coroutines.flow.MutableStateFlow
 | 
					import kotlinx.coroutines.flow.MutableStateFlow
 | 
				
			||||||
import kotlinx.coroutines.flow.asStateFlow
 | 
					import kotlinx.coroutines.flow.StateFlow
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
import okhttp3.Credentials
 | 
					 | 
				
			||||||
import retrofit2.Retrofit
 | 
					 | 
				
			||||||
import retrofit2.converter.gson.GsonConverterFactory
 | 
					 | 
				
			||||||
import ru.myitschool.work.SessionManager
 | 
					import ru.myitschool.work.SessionManager
 | 
				
			||||||
import ru.myitschool.work.api.ApiService
 | 
					import ru.myitschool.work.api.ApiService
 | 
				
			||||||
import ru.myitschool.work.core.Constants
 | 
					import ru.myitschool.work.api.UserAuthResponse
 | 
				
			||||||
import javax.inject.Inject
 | 
					import javax.inject.Inject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@HiltViewModel
 | 
					@HiltViewModel
 | 
				
			||||||
class LoginViewModel @Inject constructor(
 | 
					class LoginViewModel @Inject constructor(
 | 
				
			||||||
    @ApplicationContext private val context: Context,
 | 
					    private val apiService: ApiService
 | 
				
			||||||
) : ViewModel() {
 | 
					) : ViewModel() {
 | 
				
			||||||
    private val _state = MutableStateFlow(LoginState())
 | 
					 | 
				
			||||||
    val state = _state.asStateFlow()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val apiService: ApiService by lazy {
 | 
					    private val _state = MutableStateFlow(LoginState())
 | 
				
			||||||
        Retrofit.Builder()
 | 
					    val state: StateFlow<LoginState> get() = _state
 | 
				
			||||||
            .baseUrl(Constants.SERVER_ADDRESS)
 | 
					 | 
				
			||||||
            .addConverterFactory(GsonConverterFactory.create())
 | 
					 | 
				
			||||||
            .build()
 | 
					 | 
				
			||||||
            .create(ApiService::class.java)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun authenticate(username: String, password: String) {
 | 
					    fun authenticate(username: String, password: String) {
 | 
				
			||||||
        if (isValidUsername(username)) {
 | 
					        if (isValidUsername(username)) {
 | 
				
			||||||
            viewModelScope.launch {
 | 
					            viewModelScope.launch {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    val credentials = Credentials.basic(username, password) // Создаем Basic Auth заголовок
 | 
					                    val response = apiService.authenticate(username, password)
 | 
				
			||||||
                    val response = apiService.authenticate(username, credentials) // Передаем заголовок в запрос
 | 
					                    Log.d("LoginViewModel", "Response code: ${response.code()}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    if (response.isSuccessful) {
 | 
					                    if (response.isSuccessful) {
 | 
				
			||||||
 | 
					                        val userAuthResponse = response.body() // Получаем JSON-ответ
 | 
				
			||||||
 | 
					                        Log.d("LoginViewModel", "User  Auth Response: $userAuthResponse") // Логируем ответ
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        // Обработка JSON-ответа
 | 
				
			||||||
 | 
					                        if (userAuthResponse != null) {
 | 
				
			||||||
                            SessionManager.userLogin = username
 | 
					                            SessionManager.userLogin = username
 | 
				
			||||||
                        _state.value = LoginState(success = true)
 | 
					                            SessionManager.userRole = userAuthResponse.role // Сохраняем роль
 | 
				
			||||||
                    } else if (response.code() == 503) { // Пример кода для техработ
 | 
					                            _state.value = LoginState(success = true) // Успешная авторизация
 | 
				
			||||||
                        _state.value = LoginState(maintenance = true)
 | 
					 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                        _state.value = LoginState(error = "Ошибка авторизации")
 | 
					                            _state.value = LoginState(error = "Ошибка авторизации: Неверные учетные данные.")
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        _state.value = LoginState(error = "Ошибка авторизации: ${response.message()}")
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                } catch (e: Exception) {
 | 
					                } catch (e: Exception) {
 | 
				
			||||||
                    // Логирование ошибки
 | 
					 | 
				
			||||||
                    e.printStackTrace()
 | 
					                    e.printStackTrace()
 | 
				
			||||||
                    _state.value = LoginState(error = "Ошибка сети. Проверьте подключение к интернету.")
 | 
					                    _state.value = LoginState(error = "Ошибка сети. Проверьте подключение к интернету.")
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -57,8 +53,14 @@ class LoginViewModel @Inject constructor(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun isValidUsername(username: String): Boolean {
 | 
					    private fun isValidUsername(username: String): Boolean {
 | 
				
			||||||
        return username.length >= 3 && !username.first().isDigit() && username.all { it.isLetterOrDigit() }
 | 
					        return username.isNotEmpty() // Пример проверки логина
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    data class LoginState(val success: Boolean = false, val error: String? = null, val maintenance: Boolean = false)
 | 
					// Состояние аутентификации
 | 
				
			||||||
}
 | 
					data class LoginState(
 | 
				
			||||||
 | 
					    val success: Boolean = false, // Успешность аутентификации
 | 
				
			||||||
 | 
					    val userAuthResponse: UserAuthResponse? = null, // Ответ с информацией о пользователе
 | 
				
			||||||
 | 
					    val error: String? = null, // Сообщение об ошибке
 | 
				
			||||||
 | 
					    val maintenance: Boolean = false // Состояние техобслуживания
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@ -55,12 +55,20 @@ class QrResult : Fragment(R.layout.fragment_qr_scan_result) {
 | 
				
			|||||||
    private fun sendRequestToServer(qrData: String) {
 | 
					    private fun sendRequestToServer(qrData: String) {
 | 
				
			||||||
        lifecycleScope.launch {
 | 
					        lifecycleScope.launch {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                val response = apiService.openDoor(SessionManager.userLogin, OpenDoorRequest(qrData))
 | 
					                // Проверяем, что userLogin не равен null
 | 
				
			||||||
 | 
					                val login = SessionManager.userLogin
 | 
				
			||||||
 | 
					                if (login != null) {
 | 
				
			||||||
 | 
					                    // Создаем объект OpenDoorRequest с логином и кодом
 | 
				
			||||||
 | 
					                    val openDoorRequest = OpenDoorRequest(login, qrData.toLong()) // Преобразуем qrData в Long, если это необходимо
 | 
				
			||||||
 | 
					                    val response = apiService.openDoor(openDoorRequest) // Теперь передаем только openDoorRequest
 | 
				
			||||||
                    if (response.isSuccessful) {
 | 
					                    if (response.isSuccessful) {
 | 
				
			||||||
                        binding.result.text = "Успешно/Success"
 | 
					                        binding.result.text = "Успешно/Success"
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        binding.result.text = "Что-то пошло не так/Something wrong"
 | 
					                        binding.result.text = "Что-то пошло не так/Something wrong"
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    binding.result.text = "Пользователь не авторизован/Unauthorized user"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            } catch (e: Exception) {
 | 
					            } catch (e: Exception) {
 | 
				
			||||||
                binding.result.text = "Что-то пошло не так/Something wrong"
 | 
					                binding.result.text = "Что-то пошло не так/Something wrong"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										24
									
								
								app/src/main/java/utils/Extensions.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								app/src/main/java/utils/Extensions.kt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					package ru.myitschool.work.utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.view.View
 | 
				
			||||||
 | 
					import androidx.lifecycle.Lifecycle
 | 
				
			||||||
 | 
					import androidx.lifecycle.LifecycleOwner
 | 
				
			||||||
 | 
					import androidx.lifecycle.lifecycleScope
 | 
				
			||||||
 | 
					import androidx.lifecycle.repeatOnLifecycle
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.Flow
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.collect
 | 
				
			||||||
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Функция для сбора данных из Flow, когда жизненный цикл находится в состоянии STARTED
 | 
				
			||||||
 | 
					fun <T> Flow<T>.collectWhenStarted(lifecycleOwner: LifecycleOwner, collector: (T) -> Unit) {
 | 
				
			||||||
 | 
					    lifecycleOwner.lifecycleScope.launch {
 | 
				
			||||||
 | 
					        lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
 | 
				
			||||||
 | 
					            collect { value -> collector(value) }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Функция для управления видимостью View
 | 
				
			||||||
 | 
					fun View?.visibleOrGone(isVisible: Boolean) {
 | 
				
			||||||
 | 
					    this?.visibility = if (isVisible) View.VISIBLE else View.GONE
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -15,7 +15,7 @@
 | 
				
			|||||||
        android:text="@string/fullname_label"
 | 
					        android:text="@string/fullname_label"
 | 
				
			||||||
        android:textSize="18sp"
 | 
					        android:textSize="18sp"
 | 
				
			||||||
        android:layout_marginBottom="5dp"
 | 
					        android:layout_marginBottom="5dp"
 | 
				
			||||||
        android:visibility="visible" />
 | 
					        android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Фото пользователя. -->
 | 
					    <!-- Фото пользователя. -->
 | 
				
			||||||
    <ImageView
 | 
					    <ImageView
 | 
				
			||||||
@ -25,7 +25,7 @@
 | 
				
			|||||||
        android:layout_gravity="center"
 | 
					        android:layout_gravity="center"
 | 
				
			||||||
        android:contentDescription="@string/photo_description"
 | 
					        android:contentDescription="@string/photo_description"
 | 
				
			||||||
        android:layout_marginBottom="5dp"
 | 
					        android:layout_marginBottom="5dp"
 | 
				
			||||||
        android:visibility="visible" />
 | 
					        android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Поле для должности -->
 | 
					    <!-- Поле для должности -->
 | 
				
			||||||
    <TextView
 | 
					    <TextView
 | 
				
			||||||
@ -34,7 +34,7 @@
 | 
				
			|||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:text="@string/position_label"
 | 
					        android:text="@string/position_label"
 | 
				
			||||||
        android:layout_marginBottom="5dp"
 | 
					        android:layout_marginBottom="5dp"
 | 
				
			||||||
        android:visibility="visible" />
 | 
					        android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Поле для даты последнего входа -->
 | 
					    <!-- Поле для даты последнего входа -->
 | 
				
			||||||
    <TextView
 | 
					    <TextView
 | 
				
			||||||
@ -43,7 +43,7 @@
 | 
				
			|||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:text="2024-02-31 08:31"
 | 
					        android:text="2024-02-31 08:31"
 | 
				
			||||||
        android:layout_marginBottom="75dp"
 | 
					        android:layout_marginBottom="75dp"
 | 
				
			||||||
        android:visibility="visible" />
 | 
					        android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Кнопка обновления -->
 | 
					    <!-- Кнопка обновления -->
 | 
				
			||||||
    <Button
 | 
					    <Button
 | 
				
			||||||
@ -63,7 +63,7 @@
 | 
				
			|||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:text="@string/error_placeholder"
 | 
					        android:text="@string/error_placeholder"
 | 
				
			||||||
        android:textColor="@android:color/holo_red_dark"
 | 
					        android:textColor="@android:color/holo_red_dark"
 | 
				
			||||||
        android:visibility="visible" />
 | 
					        android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- RecyclerView для списка проходов -->
 | 
					    <!-- RecyclerView для списка проходов -->
 | 
				
			||||||
    <androidx.recyclerview.widget.RecyclerView
 | 
					    <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
@ -82,7 +82,7 @@
 | 
				
			|||||||
        android:layout_marginBottom="12dp"
 | 
					        android:layout_marginBottom="12dp"
 | 
				
			||||||
        android:backgroundTint="@color/colorPrimary"
 | 
					        android:backgroundTint="@color/colorPrimary"
 | 
				
			||||||
        android:textColor="@android:color/white"
 | 
					        android:textColor="@android:color/white"
 | 
				
			||||||
        android:visibility="visible" />
 | 
					        android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <Button
 | 
					    <Button
 | 
				
			||||||
        android:id="@+id/logout"
 | 
					        android:id="@+id/logout"
 | 
				
			||||||
@ -92,5 +92,14 @@
 | 
				
			|||||||
        android:layout_marginBottom="50dp"
 | 
					        android:layout_marginBottom="50dp"
 | 
				
			||||||
        android:backgroundTint="@color/colorPrimary"
 | 
					        android:backgroundTint="@color/colorPrimary"
 | 
				
			||||||
        android:textColor="@android:color/white"
 | 
					        android:textColor="@android:color/white"
 | 
				
			||||||
        android:visibility="visible" />
 | 
					        android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Button
 | 
				
			||||||
 | 
					        android:id="@+id/admin_panel"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:text="Admin Panel"
 | 
				
			||||||
 | 
					        android:layout_marginTop="16dp"
 | 
				
			||||||
 | 
					        android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</LinearLayout>
 | 
					</LinearLayout>
 | 
				
			||||||
 | 
				
			|||||||
@ -22,6 +22,12 @@
 | 
				
			|||||||
        android:label="Main Fragment"
 | 
					        android:label="Main Fragment"
 | 
				
			||||||
        tools:layout="@layout/fragment_main" />
 | 
					        tools:layout="@layout/fragment_main" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <fragment
 | 
				
			||||||
 | 
					        android:id="@+id/adminFragment"
 | 
				
			||||||
 | 
					        android:name="ru.myitschool.work.ui.admin.AdminFragment"
 | 
				
			||||||
 | 
					        android:label="Admin Fragment"
 | 
				
			||||||
 | 
					        tools:layout="@layout/fragment_admin" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <fragment
 | 
					    <fragment
 | 
				
			||||||
        android:id="@+id/qrResultFragment"
 | 
					        android:id="@+id/qrResultFragment"
 | 
				
			||||||
        android:name="ru.myitschool.work.ui.qr.result.QrResult"
 | 
					        android:name="ru.myitschool.work.ui.qr.result.QrResult"
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user