Merge remote-tracking branch 'origin/master' into Nymos-dev
This commit is contained in:
commit
e73263a657
@ -8,8 +8,8 @@ enum class Position {
|
|||||||
|
|
||||||
data class UserDTO(
|
data class UserDTO(
|
||||||
val firstName: String = "",
|
val firstName: String = "",
|
||||||
val secondName: String = "",
|
val lastName: String = "",
|
||||||
val thirdName: String = "",
|
val patronymic: String = "",
|
||||||
val position: Position = Position.DEVELOPER,
|
val position: Position = Position.DEVELOPER,
|
||||||
val lastVisit: LocalDateTime = LocalDateTime.now(),
|
val lastVisit: LocalDateTime = LocalDateTime.now(),
|
||||||
val isError: Boolean = false,
|
val isError: Boolean = false,
|
||||||
|
@ -10,7 +10,7 @@ interface DataRepository {
|
|||||||
suspend fun saveToken(token: String, login: String)
|
suspend fun saveToken(token: String, login: String)
|
||||||
suspend fun getToken(): String
|
suspend fun getToken(): String
|
||||||
suspend fun getLogin(): String
|
suspend fun getLogin(): String
|
||||||
suspend fun getInfo(): UserDTO
|
suspend fun getInfo(login: String): UserDTO
|
||||||
suspend fun getVisits(id: String): VisitCardWrapper
|
suspend fun getVisits(id: String): VisitCardWrapper
|
||||||
suspend fun open(): RequestResult
|
suspend fun open(): RequestResult
|
||||||
suspend fun logout()
|
suspend fun logout()
|
||||||
|
@ -35,8 +35,8 @@ class DataRepositoryImpl @Inject constructor(@ApplicationContext private val con
|
|||||||
return context.getSharedPreferences("auth", MODE_PRIVATE).getString("login", "")!!
|
return context.getSharedPreferences("auth", MODE_PRIVATE).getString("login", "")!!
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getInfo(): UserDTO {
|
override suspend fun getInfo(login: String): UserDTO {
|
||||||
val result = Provider.provideRetrofit().getInfo(getToken()).execute()
|
val result = Provider.provideRetrofit().getInfo(token = getToken(), login = login).execute()
|
||||||
return if (result.isSuccessful) result.body()!!
|
return if (result.isSuccessful) result.body()!!
|
||||||
else UserDTO(isError = true, isUnauthorized = result.code() == 403)
|
else UserDTO(isError = true, isUnauthorized = result.code() == 403)
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,11 @@ import retrofit2.http.Path
|
|||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
|
|
||||||
interface RetrofitApi {
|
interface RetrofitApi {
|
||||||
@GET("employee/admin/auth")
|
@GET("employee/auth")
|
||||||
fun auth(@Header("Authorization") token: String): Call<ResponseBody>
|
fun auth(@Header("Authorization") token: String): Call<ResponseBody>
|
||||||
|
|
||||||
@GET("employee/info")
|
@GET("employee/{login}/info")
|
||||||
fun getInfo(@Header("Authorization") token: String): Call<UserDTO>
|
fun getInfo(@Header("Authorization") token: String, @Path("login") login: String): Call<UserDTO>
|
||||||
|
|
||||||
@GET("visits/{login}/visits")
|
@GET("visits/{login}/visits")
|
||||||
fun getVisits(@Header("Authorization") token: String, @Path("login") login:String): Call<List<VisitCardDTO>>
|
fun getVisits(@Header("Authorization") token: String, @Path("login") login:String): Call<List<VisitCardDTO>>
|
||||||
|
@ -9,7 +9,8 @@ interface DomainRepository {
|
|||||||
suspend fun auth(email: String, password: String): LoginResult
|
suspend fun auth(email: String, password: String): LoginResult
|
||||||
suspend fun saveToken(token: String, login: String)
|
suspend fun saveToken(token: String, login: String)
|
||||||
suspend fun getToken(): String?
|
suspend fun getToken(): String?
|
||||||
suspend fun getInfo(): UserDTO
|
suspend fun getLogin(): String
|
||||||
|
suspend fun getInfo(login: String): UserDTO
|
||||||
suspend fun getVisits(id: String): VisitCardWrapper
|
suspend fun getVisits(id: String): VisitCardWrapper
|
||||||
suspend fun open(): RequestResult
|
suspend fun open(): RequestResult
|
||||||
suspend fun logout()
|
suspend fun logout()
|
||||||
|
@ -32,9 +32,13 @@ class DomainRepositoryImpl @Inject constructor(private val dataRepositoryImpl: D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getInfo(): UserDTO {
|
override suspend fun getLogin(): String {
|
||||||
|
return dataRepositoryImpl.getLogin()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getInfo(login: String): UserDTO {
|
||||||
return try {
|
return try {
|
||||||
return dataRepositoryImpl.getInfo()
|
return dataRepositoryImpl.getInfo(login.ifBlank { getLogin() })
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
UserDTO(isError = true, isUnauthorized = true)
|
UserDTO(isError = true, isUnauthorized = true)
|
||||||
}
|
}
|
||||||
|
@ -12,5 +12,4 @@ class LoginUseCase @Inject constructor(private val domainRepositoryImpl: DomainR
|
|||||||
suspend fun auth(email: String, password: String): LoginResult {
|
suspend fun auth(email: String, password: String): LoginResult {
|
||||||
return domainRepositoryImpl.auth(email, password)
|
return domainRepositoryImpl.auth(email, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -7,7 +7,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class ProfileUseCase @Inject constructor(private val domainRepositoryImpl: DomainRepositoryImpl) {
|
class ProfileUseCase @Inject constructor(private val domainRepositoryImpl: DomainRepositoryImpl) {
|
||||||
suspend fun getInfo(): UserDTO{
|
suspend fun getInfo(): UserDTO{
|
||||||
return domainRepositoryImpl.getInfo()
|
return domainRepositoryImpl.getInfo("")
|
||||||
}
|
}
|
||||||
suspend fun logout(){
|
suspend fun logout(){
|
||||||
return domainRepositoryImpl.logout()
|
return domainRepositoryImpl.logout()
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.nto.domain.usecase
|
||||||
|
|
||||||
|
import com.nto.domain.repository.DomainRepositoryImpl
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class SplashScreenUseCase @Inject constructor(private val domainRepositoryImpl: DomainRepositoryImpl) {
|
||||||
|
suspend fun getToken(): String? {
|
||||||
|
return domainRepositoryImpl.getToken()
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ junitVersion = "1.2.1"
|
|||||||
espressoCore = "3.6.1"
|
espressoCore = "3.6.1"
|
||||||
lifecycleRuntimeKtx = "2.8.7"
|
lifecycleRuntimeKtx = "2.8.7"
|
||||||
activityCompose = "1.10.0"
|
activityCompose = "1.10.0"
|
||||||
composeBom = "2024.04.01"
|
composeBom = "2024.09.03"
|
||||||
|
|
||||||
kapt = "2.1.10"
|
kapt = "2.1.10"
|
||||||
|
|
||||||
|
@ -1,44 +1,48 @@
|
|||||||
package com.nto.presentation
|
package com.nto.presentation
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.WindowInsets
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.LocalActivity
|
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.viewModels
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.OutlinedButton
|
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.nto.presentation.composable.Navigation
|
import com.nto.presentation.composable.Navigation
|
||||||
|
import com.nto.presentation.screens.splashScreen.SplashScreenViewModel
|
||||||
import com.nto.presentation.theme.NTOTheme
|
import com.nto.presentation.theme.NTOTheme
|
||||||
|
import com.nto.presentation.theme.TextColor
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
val viewmodel by viewModels<SplashScreenViewModel>()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
this.window.statusBarColor = TextColor.toArgb()
|
||||||
|
viewmodel.checkLogin().apply {
|
||||||
setContent {
|
setContent {
|
||||||
NTOTheme {
|
NTOTheme {
|
||||||
//XML SUCKS! We use Jetpack Compose btw :>
|
//XML SUCKS! We use Jetpack Compose btw :>
|
||||||
this.window.statusBarColor = NTOTheme.colors.primaryText.toArgb()
|
val state = viewmodel.state.collectAsState().value
|
||||||
|
|
||||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
Navigation(
|
Navigation(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.fillMaxSize()
|
.fillMaxSize(),
|
||||||
|
skipAuth = state.skipAuth
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -6,16 +6,17 @@ import androidx.navigation.NavHostController
|
|||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import com.nto.data.utils.Destinations
|
import com.nto.data.utils.Destinations
|
||||||
|
import com.nto.presentation.screens.admin.AdminScreen
|
||||||
import com.nto.presentation.screens.loginScreen.LoginScreen
|
import com.nto.presentation.screens.loginScreen.LoginScreen
|
||||||
import com.nto.presentation.screens.profileScreen.ProfileScreen
|
import com.nto.presentation.screens.profileScreen.ProfileScreen
|
||||||
import com.nto.presentation.screens.scanResult.ScanResultScreen
|
import com.nto.presentation.screens.scanResult.ScanResultScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Navigation(navController: NavHostController, modifier: Modifier = Modifier) {
|
fun Navigation(navController: NavHostController, skipAuth: Boolean, modifier: Modifier = Modifier) {
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
startDestination = Destinations.Login
|
startDestination = if (skipAuth) Destinations.Profile else Destinations.Login
|
||||||
) {
|
) {
|
||||||
composable<Destinations.Login> {
|
composable<Destinations.Login> {
|
||||||
LoginScreen(navController)
|
LoginScreen(navController)
|
||||||
@ -27,7 +28,7 @@ fun Navigation(navController: NavHostController, modifier: Modifier = Modifier)
|
|||||||
ScanResultScreen(navController)
|
ScanResultScreen(navController)
|
||||||
}
|
}
|
||||||
composable<Destinations.Admin> {
|
composable<Destinations.Admin> {
|
||||||
//TODO
|
AdminScreen(navController)
|
||||||
}
|
}
|
||||||
composable<Destinations.Options> {
|
composable<Destinations.Options> {
|
||||||
//TODO
|
//TODO
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
package com.nto.presentation.screens.admin
|
||||||
|
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import com.nto.data.utils.Destinations
|
||||||
|
import com.nto.presentation.R
|
||||||
|
import com.nto.presentation.composable.InputField
|
||||||
|
import com.nto.presentation.theme.NTOTheme
|
||||||
|
import com.nto.presentation.theme.playfair
|
||||||
|
import com.nto.presentation.theme.raleway
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun AdminScreen(
|
||||||
|
navController: NavController,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: AdminViewModel = hiltViewModel()
|
||||||
|
) {
|
||||||
|
Scaffold { innerPaddings ->
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.padding(innerPaddings)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
) {
|
||||||
|
Spacer(Modifier.height(36.dp))
|
||||||
|
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.admin_header),
|
||||||
|
fontFamily = playfair,
|
||||||
|
color = NTOTheme.colors.primaryText,
|
||||||
|
fontSize = 32.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
navController.popBackStack()
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.size(44.dp)
|
||||||
|
.border(BorderStroke(2.dp, NTOTheme.colors.buttonDisabled), CircleShape),
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.ic_arrow_back),
|
||||||
|
contentDescription = ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(Modifier.height(20.dp))
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.text_login),
|
||||||
|
fontFamily = raleway,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = NTOTheme.colors.primaryText,
|
||||||
|
modifier = Modifier.padding(start = 10.dp),
|
||||||
|
fontSize = 14.sp
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(5.dp))
|
||||||
|
InputField(
|
||||||
|
viewModel.login,
|
||||||
|
placeholder = stringResource(R.string.placholder_login),
|
||||||
|
onValueChange = viewModel::setLogin
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.nto.presentation.screens.admin
|
||||||
|
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class AdminViewModel @Inject constructor(): ViewModel() {
|
||||||
|
private var _login by mutableStateOf("")
|
||||||
|
|
||||||
|
val login get() = _login
|
||||||
|
|
||||||
|
fun setLogin(value: String) {
|
||||||
|
_login = value
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,6 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.colorResource
|
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
@ -18,7 +18,9 @@ import kotlinx.coroutines.launch
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LoginViewModel @Inject constructor(private val useCase: LoginUseCase, @ApplicationContext private val context: Context) : ViewModel() {
|
class LoginViewModel @Inject constructor(
|
||||||
|
private val useCase: LoginUseCase, @ApplicationContext private val context: Context
|
||||||
|
) : ViewModel() {
|
||||||
private val _state = MutableStateFlow(LoginScreenState())
|
private val _state = MutableStateFlow(LoginScreenState())
|
||||||
|
|
||||||
val state: StateFlow<LoginScreenState>
|
val state: StateFlow<LoginScreenState>
|
||||||
@ -52,7 +54,7 @@ class LoginViewModel @Inject constructor(private val useCase: LoginUseCase, @App
|
|||||||
if (result.successful) {
|
if (result.successful) {
|
||||||
Dispatchers.Main {
|
Dispatchers.Main {
|
||||||
navController.navigate(Destinations.Profile) {
|
navController.navigate(Destinations.Profile) {
|
||||||
popUpTo<Destinations.Login>() {
|
popUpTo<Destinations.Login> {
|
||||||
inclusive = true
|
inclusive = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
package com.nto.presentation.screens.profileScreen
|
package com.nto.presentation.screens.profileScreen
|
||||||
|
|
||||||
import android.content.ContentValues.TAG
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
@ -19,11 +17,16 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.IconButtonDefaults
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
@ -41,8 +44,8 @@ import androidx.navigation.NavController
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import com.journeyapps.barcodescanner.ScanContract
|
import com.journeyapps.barcodescanner.ScanContract
|
||||||
import com.journeyapps.barcodescanner.ScanOptions
|
import com.journeyapps.barcodescanner.ScanOptions
|
||||||
import com.nto.presentation.CustomCaptureActivity
|
|
||||||
import com.nto.data.utils.Destinations
|
import com.nto.data.utils.Destinations
|
||||||
|
import com.nto.presentation.CustomCaptureActivity
|
||||||
import com.nto.presentation.R
|
import com.nto.presentation.R
|
||||||
import com.nto.presentation.composable.DecoratedButton
|
import com.nto.presentation.composable.DecoratedButton
|
||||||
import com.nto.presentation.composable.cards.VisitCard
|
import com.nto.presentation.composable.cards.VisitCard
|
||||||
@ -51,6 +54,7 @@ import com.nto.presentation.theme.TextGray
|
|||||||
import com.nto.presentation.theme.playfair
|
import com.nto.presentation.theme.playfair
|
||||||
import com.nto.presentation.theme.raleway
|
import com.nto.presentation.theme.raleway
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ProfileScreen(
|
fun ProfileScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
@ -58,7 +62,6 @@ fun ProfileScreen(
|
|||||||
viewModel: ProfileViewModel = hiltViewModel<ProfileViewModel>(),
|
viewModel: ProfileViewModel = hiltViewModel<ProfileViewModel>(),
|
||||||
) {
|
) {
|
||||||
val state = viewModel.state.collectAsState().value
|
val state = viewModel.state.collectAsState().value
|
||||||
|
|
||||||
val scannerLauncher = rememberLauncherForActivityResult(
|
val scannerLauncher = rememberLauncherForActivityResult(
|
||||||
contract = ScanContract(),
|
contract = ScanContract(),
|
||||||
onResult = { result -> navController.navigate(Destinations.Scan(result.contents)) }
|
onResult = { result -> navController.navigate(Destinations.Scan(result.contents)) }
|
||||||
@ -75,14 +78,23 @@ fun ProfileScreen(
|
|||||||
navController.navigate(Destinations.Login)
|
navController.navigate(Destinations.Login)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PullToRefreshBox(
|
||||||
|
isRefreshing = viewModel.isLoading,
|
||||||
|
onRefresh = {
|
||||||
|
viewModel.updateInfo()
|
||||||
|
}
|
||||||
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.background(NTOTheme.colors.primaryBackground)
|
modifier = modifier
|
||||||
|
.background(NTOTheme.colors.primaryBackground)
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(start = 20.dp, end = 20.dp)
|
.padding(start = 20.dp, end = 20.dp)
|
||||||
|
.verticalScroll(
|
||||||
|
rememberScrollState()
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
Spacer(modifier = Modifier.height(10.dp))
|
Spacer(modifier = Modifier.height(10.dp))
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
@ -99,63 +111,54 @@ fun ProfileScreen(
|
|||||||
fontWeight = FontWeight.Bold
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||||
Box(
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
navController.navigate(Destinations.Admin)
|
||||||
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(35.dp)
|
.size(44.dp)
|
||||||
.clip(CircleShape)
|
.border(
|
||||||
.border(2.dp, NTOTheme.colors.buttonAdmin, shape = CircleShape),
|
BorderStroke(2.dp, NTOTheme.colors.buttonAdmin),
|
||||||
contentAlignment = Alignment.Center
|
CircleShape
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
IconButton(modifier = Modifier.size(25.dp), onClick = {
|
|
||||||
viewModel.admin(navController)
|
|
||||||
}) {
|
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.icon_admin),
|
painter = painterResource(R.drawable.icon_admin),
|
||||||
modifier = Modifier.size(20.dp),
|
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = NTOTheme.colors.buttonAdmin
|
tint = NTOTheme.colors.buttonAdmin
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
IconButton(
|
||||||
Box(
|
onClick = {},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(35.dp)
|
.size(44.dp)
|
||||||
.clip(CircleShape)
|
.border(
|
||||||
.border(2.dp, NTOTheme.colors.buttonDisabled, shape = CircleShape),
|
BorderStroke(2.dp, NTOTheme.colors.buttonDisabled),
|
||||||
contentAlignment = Alignment.Center
|
CircleShape
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
IconButton(modifier = Modifier.size(25.dp), onClick = {
|
|
||||||
viewModel.option(navController)
|
|
||||||
}) {
|
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.icon_options),
|
painter = painterResource(R.drawable.icon_options),
|
||||||
modifier = Modifier.size(20.dp),
|
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = NTOTheme.colors.buttonDisabled
|
tint = NTOTheme.colors.buttonDisabled
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
Spacer(modifier = Modifier.width(14.dp))
|
IconButton(
|
||||||
Box(
|
modifier = Modifier.size(44.dp), onClick = {
|
||||||
modifier = Modifier
|
|
||||||
.size(35.dp)
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(NTOTheme.colors.buttonDisabled),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
IconButton(modifier = Modifier.size(25.dp), onClick = {
|
|
||||||
viewModel.logout(navController)
|
viewModel.logout(navController)
|
||||||
}) {
|
}, colors = IconButtonDefaults.iconButtonColors(
|
||||||
|
containerColor = NTOTheme.colors.buttonDisabled
|
||||||
|
)
|
||||||
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.icon_logout),
|
painter = painterResource(R.drawable.icon_logout),
|
||||||
modifier = Modifier.size(20.dp),
|
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = NTOTheme.colors.primaryBackground
|
tint = NTOTheme.colors.primaryBackground
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(50.dp))
|
Spacer(modifier = Modifier.height(50.dp))
|
||||||
@ -270,6 +273,8 @@ fun ProfileScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun ProfileScreenPreview() {
|
private fun ProfileScreenPreview() {
|
||||||
|
@ -5,7 +5,6 @@ import com.nto.data.models.Position
|
|||||||
import com.nto.data.models.UserDTO
|
import com.nto.data.models.UserDTO
|
||||||
import com.nto.data.models.cards.VisitCardDTO
|
import com.nto.data.models.cards.VisitCardDTO
|
||||||
import com.nto.presentation.R
|
import com.nto.presentation.R
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
data class ProfileState(
|
data class ProfileState(
|
||||||
@ -20,9 +19,13 @@ data class ProfileState(
|
|||||||
fun deserialize(o: UserDTO, context: Context) {
|
fun deserialize(o: UserDTO, context: Context) {
|
||||||
val dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
val dateFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||||
this.firstName = o.firstName
|
this.firstName = o.firstName
|
||||||
this.secondName = o.secondName
|
this.secondName = o.lastName
|
||||||
this.thirdName = o.thirdName
|
this.thirdName = o.patronymic
|
||||||
this.lastOpen = o.lastVisit.format(dateFormat)
|
this.lastOpen = try {
|
||||||
|
o.lastVisit.format(dateFormat)
|
||||||
|
} catch (e: NullPointerException) {
|
||||||
|
context.getString(R.string.label_last_visit_none)
|
||||||
|
}
|
||||||
this.job = translatePosition(o.position, context)
|
this.job = translatePosition(o.position, context)
|
||||||
this.isUnauthorized = o.isUnauthorized
|
this.isUnauthorized = o.isUnauthorized
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package com.nto.presentation.screens.profileScreen
|
package com.nto.presentation.screens.profileScreen
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
@ -24,33 +27,20 @@ import javax.inject.Inject
|
|||||||
class ProfileViewModel @Inject constructor(
|
class ProfileViewModel @Inject constructor(
|
||||||
private val useCase: ProfileUseCase, @ApplicationContext private val context: Context
|
private val useCase: ProfileUseCase, @ApplicationContext private val context: Context
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _state = MutableStateFlow(
|
private var _isLoading by mutableStateOf(false)
|
||||||
ProfileState(
|
private val _state = MutableStateFlow(ProfileState())
|
||||||
"Николай",
|
|
||||||
"Одегов",
|
|
||||||
"Алексеевич",
|
|
||||||
"20 января 2024 20:21",
|
|
||||||
"Senior UI/UX designer",
|
|
||||||
visits = listOf(
|
|
||||||
VisitCardDTO(
|
|
||||||
"Кабинет 207",
|
|
||||||
10020,
|
|
||||||
LocalDateTime.now(),
|
|
||||||
VisitType.CARD_ENTRY,
|
|
||||||
qrCode = QRDTO(12032, "Кобинет 52")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
val isLoading get() = _isLoading
|
||||||
val state: StateFlow<ProfileState>
|
val state: StateFlow<ProfileState>
|
||||||
get() = _state.asStateFlow()
|
get() = _state.asStateFlow()
|
||||||
|
|
||||||
fun updateInfo() {
|
fun updateInfo() {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
_isLoading = true
|
||||||
val result = useCase.getInfo()
|
val result = useCase.getInfo()
|
||||||
//val visits = useCase.get
|
//val visits = useCase.get
|
||||||
_state.tryEmit(ProfileState().apply { deserialize(result, context) })
|
_state.tryEmit(ProfileState().apply { deserialize(result, context) })
|
||||||
|
_isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.nto.presentation.screens.splashScreen
|
||||||
|
|
||||||
|
data class SplashScreenState(
|
||||||
|
val skipAuth: Boolean = false
|
||||||
|
)
|
@ -0,0 +1,31 @@
|
|||||||
|
package com.nto.presentation.screens.splashScreen
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.nto.domain.usecase.SplashScreenUseCase
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class SplashScreenViewModel @Inject constructor(private val useCase: SplashScreenUseCase) : ViewModel(){
|
||||||
|
private val _state = MutableStateFlow(
|
||||||
|
SplashScreenState()
|
||||||
|
)
|
||||||
|
|
||||||
|
val state: StateFlow<SplashScreenState>
|
||||||
|
get() = _state.asStateFlow()
|
||||||
|
|
||||||
|
fun checkLogin(){
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val token = useCase.getToken()
|
||||||
|
if (!token.isNullOrBlank()){
|
||||||
|
_state.tryEmit(_state.value.copy(skipAuth = true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ val Green = Color(0xFF738D73)
|
|||||||
val GreenDisabled = Color(0xFFCAD5CA)
|
val GreenDisabled = Color(0xFFCAD5CA)
|
||||||
val Error = Color(0xFFD28989)
|
val Error = Color(0xFFD28989)
|
||||||
val Warning = Color(0xFFCFC37F)
|
val Warning = Color(0xFFCFC37F)
|
||||||
|
val TextColor = Color(0xFF211A1D)
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class AppColors(
|
data class AppColors(
|
||||||
@ -51,7 +52,7 @@ val extendedColor = AppColors(
|
|||||||
secondaryBackground = Color.Black,
|
secondaryBackground = Color.Black,
|
||||||
inputFieldBackground = BoxGray,
|
inputFieldBackground = BoxGray,
|
||||||
disabledText = TextGray,
|
disabledText = TextGray,
|
||||||
primaryText = Color.Black,
|
primaryText = TextColor,
|
||||||
secondaryText = Color.White,
|
secondaryText = Color.White,
|
||||||
button = Green,
|
button = Green,
|
||||||
buttonDisabled = GreenDisabled,
|
buttonDisabled = GreenDisabled,
|
||||||
|
9
presentation/src/main/res/drawable/ic_arrow_back.xml
Normal file
9
presentation/src/main/res/drawable/ic_arrow_back.xml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M7.825,13L13.425,18.6L12,20L4,12L12,4L13.425,5.4L7.825,11H20V13H7.825Z"
|
||||||
|
android:fillColor="#CAD5CA"/>
|
||||||
|
</vector>
|
@ -22,4 +22,6 @@
|
|||||||
<string name="code_scanned_error">Enter was cancelled</string>
|
<string name="code_scanned_error">Enter was cancelled</string>
|
||||||
<string name="code_scanned_warning">Something went wrong</string>
|
<string name="code_scanned_warning">Something went wrong</string>
|
||||||
<string name="close">Close</string>
|
<string name="close">Close</string>
|
||||||
|
<string name="label_last_visit_none">None</string>
|
||||||
|
<string name="admin_header">Admin</string>
|
||||||
</resources>
|
</resources>
|
@ -23,4 +23,6 @@
|
|||||||
<string name="code_scanned_error">Вход был отменён</string>
|
<string name="code_scanned_error">Вход был отменён</string>
|
||||||
<string name="code_scanned_warning">Что-то пошло не так</string>
|
<string name="code_scanned_warning">Что-то пошло не так</string>
|
||||||
<string name="close">Закрыть</string>
|
<string name="close">Закрыть</string>
|
||||||
|
<string name="admin_header">Управление</string>
|
||||||
|
<string name="label_last_visit_none">Нет</string>
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user