Compare commits

...

2 Commits

41 changed files with 1000 additions and 367 deletions

View File

@ -24,22 +24,22 @@ interface AppService {
@GET("users")
suspend fun getAllUsers(): List<UserRemote>
@GET("rents")
suspend fun getAllRents(): List<RentRemote>
@GET("rents")
suspend fun getRents(
@Query("_page") page: Int,
@Query("_limit") limit: Int,
): List<RentRemote>
@GET("searchFlights")
suspend fun foundFlights(
@GET("flights")
suspend fun findFlights(
@Query("direction_from") direction_from: String,
@Query("direction_to") direction_to: String,
@Query("departure_date") departure_date: String
): List<FlightRemote>
// suspend fun findFlights(
// @Path("direction_from") from: String,
// @Path("direction_to") to: String,
// @Path("departure_date") departureDate: String
// ): List<FlightRemote>
@GET("tickets")
suspend fun getAllTickets(): List<TicketRemote>
@GET("foundFlights")
@GET("flights")
suspend fun getAllFlights(): List<FlightRemote>
@GET("tickets")
suspend fun getTickets(
@ -55,8 +55,8 @@ interface AppService {
@GET("tickets")
suspend fun getFlightsTickets(@Query("flightId") flightId: Int): List<TicketRemote>
@GET("userrents/{userId}")
suspend fun getUserRents(@Path("userId") userId: Int): List<RentRemote>
@GET("rents")
suspend fun getUserRents(@Query("userId") userId: Int): List<RentRemote>
@GET("users/{id}")
suspend fun getUser(
@ -131,7 +131,7 @@ interface AppService {
@Path("id") id: Int,
): TicketRemote
companion object : AppService {
companion object {
private const val BASE_URL = "http://192.168.1.100:8079/"
@Volatile

View File

@ -9,6 +9,7 @@ import retrofit2.HttpException
import ru.ulstu.`is`.airticketrentservice.api.AppService
import ru.ulstu.`is`.airticketrentservice.api.model.toRent
import ru.ulstu.`is`.airticketrentservice.api.repository.RestTicketRepository
import ru.ulstu.`is`.airticketrentservice.api.repository.RestUserRepository
import ru.ulstu.`is`.airticketrentservice.database.AppDatabase
import ru.ulstu.`is`.airticketrentservice.database.repository.OfflineRemoteKeyRepository
import ru.ulstu.`is`.airticketrentservice.database.repository.OfflineRentRepository
@ -22,6 +23,7 @@ class RentRemoteMediator(
private val service: AppService,
private val dbRentRepository: OfflineRentRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val userRestRepository: RestUserRepository,
private val ticketRestRepository: RestTicketRepository,
private val database: AppDatabase
) : RemoteMediator<Int, Rent>() {
@ -71,8 +73,9 @@ class RentRemoteMediator(
nextKey = nextKey
)
}
userRestRepository.getAllUsers()
ticketRestRepository.getAllTickets()
dbRemoteKeyRepository.createRemoteKeys(keys)
ticketRestRepository.getTickets()
dbRentRepository.insertRents(rents)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)

View File

@ -9,6 +9,7 @@ import retrofit2.HttpException
import ru.ulstu.`is`.airticketrentservice.api.AppService
import ru.ulstu.`is`.airticketrentservice.api.model.toTicket
import ru.ulstu.`is`.airticketrentservice.api.repository.RestFlightRepository
import ru.ulstu.`is`.airticketrentservice.api.repository.RestUserRepository
import ru.ulstu.`is`.airticketrentservice.database.AppDatabase
import ru.ulstu.`is`.airticketrentservice.database.models.RemoteKeyType
import ru.ulstu.`is`.airticketrentservice.database.models.RemoteKeys
@ -71,7 +72,7 @@ class TicketRemoteMediator(
nextKey = nextKey
)
}
flightRestRepository.getFlights()
flightRestRepository.getAllFlights()
dbRemoteKeyRepository.createRemoteKeys(keys)
dbTicketRepository.insertTickets(tickets)
}

View File

@ -35,21 +35,3 @@ fun Flight.toFlightRemote(): FlightRemote = FlightRemote(
tickets_count,
one_ticket_cost
)
fun foundFlights(
from: String,
to: String,
departureDate: String
): List<Flight> {
val json = File("C:\\Users\\User\\AndroidStudioProjects\\AirTicketRentService\\server").readText()
val flights = Json.decodeFromString<List<Flight>>(json)
val matchingFlights = flights.filter { flight ->
flight.direction_from == from ||
flight.direction_to == to ||
flight.departure_date == departureDate
}
return matchingFlights
}

View File

@ -61,26 +61,61 @@ class RestFlightRepository(
override suspend fun getFlightById(flightId: Int): Flight =
service.getFlight(flightId).toFlight()
override suspend fun foundFlights(
// override suspend fun findFlights(
// from: String,
// to: String,
// departureDate: String
// ): List<Flight> {
// val existFlights = dbFlightRepository.getAllFlights().associateBy { it.id }.toMutableMap()
//
// service.findFlights(from, to, departureDate)
// .map { it.toFlight() }
// .forEach { flight ->
// existFlights[flight.id] = flight
// }
// return existFlights.map { it.value }.sortedBy { it.id }
// }
override suspend fun findFlights(
from: String,
to: String,
departureDate: String
): List<Flight> {
return try {
val foundFlights = service.findFlights(from, to, departureDate)
if (foundFlights.isNotEmpty()) {
foundFlights.map { it.toFlight() }
} else {
emptyList()
}
} catch (e: Exception) {
e.message?.let { Log.d(RestFlightRepository::class.simpleName, it) }
emptyList()
}
}
override suspend fun getAllFlights(): List<Flight> {
Log.d(RestFlightRepository::class.simpleName, "Get all flights")
val existFlights = dbFlightRepository.getAllFlights().associateBy { it.id }.toMutableMap()
service.foundFlights()
service.getAllFlights()
.map { it.toFlight() }
.forEach { flight ->
flight.direction_from == from ||
flight.direction_to == to ||
flight.departure_date == departureDate
val existFlight = existFlights[flight.id]
if (existFlight == null) {
dbFlightRepository.insertFlight(flight)
} else if (existFlight != flight) {
dbFlightRepository.updateFlight(flight)
}
existFlights[flight.id] = flight
}
return existFlights.map { it.value }.sortedBy { it.id }
}
// flight.direction_from == from ||
// flight.direction_to == to ||
// flight.departure_date == departureDate
// override suspend fun findFlights(
// from: String,

View File

@ -21,6 +21,7 @@ class RestRentRepository(
private val service: AppService,
private val dbRentRepository: OfflineRentRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val userRestRepository: RestUserRepository,
private val ticketRestRepository: RestTicketRepository,
private val database: AppDatabase
) : RentRepository {
@ -39,6 +40,7 @@ class RestRentRepository(
service,
dbRentRepository,
dbRemoteKeyRepository,
userRestRepository,
ticketRestRepository,
database
),
@ -61,21 +63,21 @@ class RestRentRepository(
rent.id.let { service.deleteRent(it).toRent() }
}
// override suspend fun getAllRents(): List<Rent> {
// val existRents = dbRentRepository.getAllRents().associateBy { it.id }.toMutableMap()
//
// service.getAllRents()
// .map { it.toRent() }
// .forEach { rent ->
// val existRent = existRents[rent.id]
// if (existRent == null) {
// dbRentRepository.insertRent(rent)
// } else if (existRent != rent) {
// dbRentRepository.updateRent(rent)
// }
// existRents[rent.id] = rent
// }
//
// return existRents.map { it.value }.sortedBy { it.id }
// }
override suspend fun getAllRents(): List<Rent> {
val existRents = dbRentRepository.getAllRents().associateBy { it.id }.toMutableMap()
service.getAllRents()
.map { it.toRent() }
.forEach { rent ->
val existRent = existRents[rent.id]
if (existRent == null) {
dbRentRepository.insertRent(rent)
} else if (existRent != rent) {
dbRentRepository.updateRent(rent)
}
existRents[rent.id] = rent
}
return existRents.map { it.value }.sortedBy { it.id }
}
}

View File

@ -2,9 +2,11 @@ package ru.ulstu.`is`.airticketrentservice.api.repository
import android.util.Log
import ru.ulstu.`is`.airticketrentservice.api.AppService
import ru.ulstu.`is`.airticketrentservice.api.model.toFlight
import ru.ulstu.`is`.airticketrentservice.api.model.toRent
import ru.ulstu.`is`.airticketrentservice.api.model.toUser
import ru.ulstu.`is`.airticketrentservice.api.model.toUserRemote
import ru.ulstu.`is`.airticketrentservice.database.models.Flight
import ru.ulstu.`is`.airticketrentservice.database.models.Rent
import ru.ulstu.`is`.airticketrentservice.database.models.User
import ru.ulstu.`is`.airticketrentservice.database.repository.OfflineRentRepository
@ -56,26 +58,40 @@ class RestUserRepository(
return existUsers.map { it.value }.sortedBy { it.id }
}
override suspend fun getUserRents(id: Int): List<Rent> {
Log.d(RestUserRepository::class.simpleName, "Get users $id rents")
val existRents = dbUserRepository.getUserRents(id).associateBy { it.id }.toMutableMap()
// override suspend fun getUserRents(userId: Int): List<Rent> {
// Log.d(RestUserRepository::class.simpleName, "Get user's $userId rents")
// val existRents = dbUserRepository.getUserRents(userId).associateBy { it.id }.toMutableMap()
//
// service.getUserRents(userId)
// .map { it.toRent() }
// .forEach { rent ->
// Log.d(RestUserRepository::class.simpleName, "айди брони: ${rent.id}, и пользователя: ${rent.userId}")
// if(rent.userId == userId) {
//// val existRent = existRents[rent.id]
//// if (existRent == null) {
//// Log.d(RestUserRepository::class.simpleName, "бронирования нет в бд")
//// dbRentRepository.insertRent(rent)
//// } else if (existRent != rent) {
//// Log.d(RestUserRepository::class.simpleName, "бронирование есть в бд")
//// dbRentRepository.updateRent(rent)
//// }
// existRents[rent.id] = rent
// }
// }
// return existRents.map { it.value }.sortedBy { it.id }
// }
service.getUserRents(id)
.map { it.toRent() }
.forEach { rent ->
Log.d(RestUserRepository::class.simpleName, "айди брони: ${rent.id}, и пользователя: ${rent.userId}")
if(rent.userId == id) {
val existRent = existRents[rent.id]
if (existRent == null) {
Log.d(RestUserRepository::class.simpleName, "бронирования нет в бд")
dbRentRepository.insertRent(rent)
} else if (existRent != rent) {
Log.d(RestUserRepository::class.simpleName, "бронирование есть в бд")
dbRentRepository.updateRent(rent)
override suspend fun getUserRents(userId: Int): List<Rent> {
return try {
val usersRents = service.getUserRents(userId)
if (usersRents.isNotEmpty()) {
usersRents.map { it.toRent() }
} else {
emptyList()
}
existRents[rent.id] = rent
} catch (e: Exception) {
e.message?.let { Log.d(RestUserRepository::class.simpleName, it) }
emptyList()
}
}
return existRents.map { it.value }.sortedBy { it.id }
}
}

View File

@ -68,6 +68,7 @@ class AppDataContainer(private val context: Context) : AppContainer {
AppService.getInstance(),
rentRepository,
remoteKeyRepository,
userRestRepository,
ticketRestRepository,
AppDatabase.getInstance(context)
)

View File

@ -24,7 +24,7 @@ abstract class AppDatabase : RoomDatabase() {
abstract fun remoteKeysDao(): RemoteKeysDao
companion object {
private const val DB_NAME: String = "ticketservicedatabase"
private const val DB_NAME: String = "ticketservicedatabase5"
@Volatile
private var INSTANCE: AppDatabase? = null

View File

@ -12,7 +12,7 @@ import ru.ulstu.`is`.airticketrentservice.database.models.Flight
@Dao
interface FlightDao {
@Query("select * from flights")
fun getAll(): List<Flight>
suspend fun getAll(): List<Flight>
@Query("select * from flights where flights.id = :flightId")
fun getFlightById(flightId: Int): Flow<Flight>

View File

@ -11,8 +11,8 @@ import ru.ulstu.`is`.airticketrentservice.database.models.Rent
@Dao
interface RentDao {
// @Query("select * from rents")
// fun getAll(): List<Rent>
@Query("select * from rents")
fun getAll(): List<Rent>
@Query("select * from rents where rents.id = :rentId")
fun getRentById(rentId: Int): Flow<Rent>

View File

@ -3,6 +3,7 @@ package ru.ulstu.`is`.airticketrentservice.database.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = "rents",
@ -11,14 +12,17 @@ import androidx.room.PrimaryKey
entity = User::class,
parentColumns = ["id"],
childColumns = ["user_id"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT),
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE),
ForeignKey(
entity = Ticket::class,
parentColumns = ["id"],
childColumns = ["ticket_id"],
onDelete = ForeignKey.RESTRICT,
onUpdate = ForeignKey.RESTRICT)
onDelete = ForeignKey.CASCADE,
onUpdate = ForeignKey.CASCADE)
],
indices = [
Index(value = ["ticket_id"], unique = true)
])
data class Rent(
@PrimaryKey(autoGenerate = true)

View File

@ -3,6 +3,7 @@ package ru.ulstu.`is`.airticketrentservice.database.models
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
@Entity(tableName = "tickets",

View File

@ -5,10 +5,11 @@ import kotlinx.coroutines.flow.Flow
import ru.ulstu.`is`.airticketrentservice.database.models.Flight
interface FlightRepository {
suspend fun getAllFlights(): List<Flight>
suspend fun insertFlight(flight: Flight)
suspend fun updateFlight(flight: Flight)
suspend fun deleteFlight(flight: Flight)
suspend fun getFlightById(flightId: Int): Flight
suspend fun foundFlights(from: String, to: String, departureDate: String): List<Flight>
suspend fun findFlights(from: String, to: String, departureDate: String): List<Flight>
fun getFlights(): Flow<PagingData<Flight>>
}

View File

@ -11,7 +11,7 @@ import ru.ulstu.`is`.airticketrentservice.database.models.Flight
import ru.ulstu.`is`.airticketrentservice.database.models.Rent
class OfflineFlightRepository(private val flightDao: FlightDao) : FlightRepository{
suspend fun getAllFlights(): List<Flight> = flightDao.getAll()
override suspend fun getAllFlights(): List<Flight> = flightDao.getAll()
override suspend fun insertFlight(flight: Flight) = flightDao.insert(flight)
override suspend fun updateFlight(flight: Flight) = flightDao.update(flight)
override suspend fun deleteFlight(flight: Flight) = flightDao.delete(flight)
@ -25,7 +25,7 @@ class OfflineFlightRepository(private val flightDao: FlightDao) : FlightReposito
).flow
fun getAllFlightsPagingSource(): PagingSource<Int, Flight> = flightDao.getFlights()
override suspend fun foundFlights(from: String, to: String, departureDate: String): List<Flight> = flightDao.findFlights(from, to, departureDate)
override suspend fun findFlights(from: String, to: String, departureDate: String): List<Flight> = flightDao.findFlights(from, to, departureDate)
suspend fun clearFlights() = flightDao.deleteAll()
suspend fun insertFlights(flights: List<Flight>) =
flightDao.insert(*flights.toTypedArray())

View File

@ -14,7 +14,7 @@ class OfflineRentRepository(private val rentDao: RentDao) : RentRepository{
override suspend fun insertRent(rent: Rent) = rentDao.insert(rent)
override suspend fun updateRent(rent: Rent) = rentDao.update(rent)
override suspend fun deleteRent(rent: Rent) = rentDao.delete(rent)
// override suspend fun getAllRents(): List<Rent> = rentDao.getAll()
override suspend fun getAllRents(): List<Rent> = rentDao.getAll()
override suspend fun getRentById(rentId: Int): Rent = rentDao.getRentById(rentId).first()
override fun getRents(): Flow<PagingData<Rent>> = Pager(
config = PagingConfig(

View File

@ -9,7 +9,7 @@ interface RentRepository {
suspend fun insertRent(rent: Rent)
suspend fun updateRent(rent: Rent)
suspend fun deleteRent(rent: Rent)
// suspend fun getAllRents(): List<Rent>
suspend fun getAllRents(): List<Rent>
suspend fun getRentById(rentId: Int): Rent
fun getRents(): Flow<PagingData<Rent>>
}

View File

@ -9,5 +9,5 @@ interface UserRepository {
suspend fun deleteUser(user: User)
suspend fun getAllUsers(): List<User>
suspend fun getUserById(userId: Int): User
suspend fun getUserRents(id: Int): List<Rent>
suspend fun getUserRents(userId: Int): List<Rent>
}

View File

@ -10,6 +10,7 @@ import androidx.navigation.navArgument
import ru.ulstu.`is`.airticketrentservice.navigation.BottomBarScreen
import ru.ulstu.`is`.airticketrentservice.screen.Admin
import ru.ulstu.`is`.airticketrentservice.screen.FlightEdit
import ru.ulstu.`is`.airticketrentservice.screen.FlightInfo
import ru.ulstu.`is`.airticketrentservice.screen.FlightList
import ru.ulstu.`is`.airticketrentservice.screen.FoundFlights
import ru.ulstu.`is`.airticketrentservice.screen.MainPage
@ -18,6 +19,8 @@ import ru.ulstu.`is`.airticketrentservice.screen.Profile
import ru.ulstu.`is`.airticketrentservice.screen.RentEdit
import ru.ulstu.`is`.airticketrentservice.screen.RentList
import ru.ulstu.`is`.airticketrentservice.screen.TicketEdit
import ru.ulstu.`is`.airticketrentservice.screen.TicketList
import ru.ulstu.`is`.airticketrentservice.screen.TicketView
import ru.ulstu.`is`.airticketrentservice.screen.UserEdit
import ru.ulstu.`is`.airticketrentservice.screen.UserList
import ru.ulstu.`is`.airticketrentservice.viewModel.AppViewModelProvider
@ -61,12 +64,17 @@ fun HomeNavGraph(
composable(
route = BottomBarScreen.UserList.route
){
UserList(navController)
UserList(navController, userListViewModel)
}
composable(
route = BottomBarScreen.TicketList.route
){
TicketList(navController, flightListViewModel, ticketListViewModel)
}
composable(
route = BottomBarScreen.RentList.route
){
RentList(navController, rentListViewModel, ticketListViewModel)
RentList(navController, userListViewModel, currentUserViewModel, ticketListViewModel, rentListViewModel)
}
composable(
route = BottomBarScreen.Admin.route
@ -113,6 +121,16 @@ fun HomeNavGraph(
){
FlightEdit(navController)
}
composable(
route = BottomBarScreen.FlightInfo.route,
arguments = listOf(
navArgument("id") {
type = NavType.IntType
}
)
){
FlightInfo(navController)
}
composable(
route = BottomBarScreen.MyRents.route,
arguments = listOf(
@ -131,5 +149,13 @@ fun HomeNavGraph(
) {
TicketEdit(navController)
}
composable(
route = BottomBarScreen.TicketView.route,
arguments = listOf(
navArgument("id") { type = NavType.IntType }
)
) {
TicketView(navController)
}
}
}

View File

@ -14,6 +14,7 @@ import ru.ulstu.`is`.airticketrentservice.viewModel.FlightListViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.LoginViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.RegistrationViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.RentListViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.TicketListViewModel
//import ru.ulstu.`is`.airticketrentservice.viewModel.TicketEditViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.UserListViewModel
@ -22,11 +23,12 @@ const val USERID_ARGUMENT="userId"
@Composable
fun RootNavigationGraph(
navController: NavHostController,
userListViewModel: UserListViewModel = viewModel(factory = AppViewModelProvider.Factory),
currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory),
registrationViewModel: RegistrationViewModel= viewModel(factory = AppViewModelProvider.Factory),
loginViewModel: LoginViewModel = viewModel(factory = AppViewModelProvider.Factory),
flightListViewModel: FlightListViewModel = viewModel(factory = AppViewModelProvider.Factory),
userListViewModel: UserListViewModel = viewModel(factory = AppViewModelProvider.Factory),
ticketListViewModel: TicketListViewModel = viewModel(factory = AppViewModelProvider.Factory),
rentListViewModel: RentListViewModel = viewModel(factory = AppViewModelProvider.Factory)
){
NavHost(
@ -43,6 +45,7 @@ fun RootNavigationGraph(
currentUserViewModel = currentUserViewModel,
flightListViewModel = flightListViewModel,
userListViewModel = userListViewModel,
ticketListViewModel = ticketListViewModel,
rentListViewModel = rentListViewModel
)
}

View File

@ -39,6 +39,11 @@ sealed class BottomBarScreen(
title ="",
icon = Icons.Filled.AccountCircle
)
object TicketList: BottomBarScreen(
route = "ticket-list",
title ="",
icon = Icons.Filled.AccountCircle
)
object RentList: BottomBarScreen(
route = "rent-list",
title ="",
@ -80,6 +85,15 @@ sealed class BottomBarScreen(
return "flight-edit/$id"
}
}
object TicketView: BottomBarScreen(
route = "ticket-view/{id}",
title ="",
icon = Icons.Filled.AccountCircle
) {
fun passId(id: String): String{
return "ticket-view/$id"
}
}
object FlightInfo: BottomBarScreen(
route = "flight-info/{id}",
title ="",

View File

@ -79,5 +79,22 @@ fun Admin (
) {
Text("Бронирования")
}
Button(
onClick = { navController.navigate(BottomBarScreen.TicketList.route)},
colors = ButtonDefaults.buttonColors(
containerColor = (colorResource(id = R.color.lightBlue)),
contentColor = Color.White
),
elevation = ButtonDefaults.buttonElevation(
defaultElevation = 10.dp,
pressedElevation = 6.dp
),
shape = RoundedCornerShape(15.dp),
modifier = Modifier
.fillMaxWidth()
.padding(all = 10.dp),
) {
Text("Билеты")
}
}
}

View File

@ -40,12 +40,14 @@ import java.util.Date
@Composable
fun FlightInfo(
navController: NavController,
flightViewModel: FlightEditViewModel = viewModel(factory = AppViewModelProvider.Factory)
flightViewModel: FlightEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
ticketViewModel: TicketEditViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
FlightInfo(
flightUiState = flightViewModel.flightUiState,
onClick = {
val route = BottomBarScreen.TicketEdit.passId(0.toString())
flightViewModel.setCurrentFlight(ticketViewModel.ticketUiState.ticketDetails.flightId)
val route = BottomBarScreen.TicketView.passId(0.toString())
navController.navigate(route)
}
)
@ -78,7 +80,8 @@ private fun FlightInfo(
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_from)) },
singleLine = true
singleLine = true,
readOnly = true
)
TextField(
modifier = Modifier
@ -93,12 +96,43 @@ private fun FlightInfo(
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_to)) },
singleLine = true
singleLine = true,
readOnly = true
)
Text(text = " ${flightUiState.flightDetails.departure_date}", fontSize = 15.sp, color = Color.DarkGray)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = flightUiState.flightDetails.departure_date,
onValueChange = { },
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_arrivalDate)) },
singleLine = true,
readOnly = true
)
Text(text = flightUiState.flightDetails.arrival_date, fontSize = 15.sp, color = Color.DarkGray)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = flightUiState.flightDetails.arrival_date,
onValueChange = { },
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_departureDate)) },
singleLine = true,
readOnly = true
)
TextField(
modifier = Modifier
@ -113,7 +147,8 @@ private fun FlightInfo(
),
shape = RoundedCornerShape(15.dp),
label = { Text("Количество билетов") },
singleLine = true
singleLine = true,
readOnly = true
)
TextField(
@ -129,7 +164,8 @@ private fun FlightInfo(
),
shape = RoundedCornerShape(15.dp),
label = { Text("Стоимость одного билета") },
singleLine = true
singleLine = true,
readOnly = true
)
Button(
onClick = onClick,

View File

@ -46,7 +46,7 @@ fun FoundFlights(
navController: NavController,
viewModel: FindFlightsViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val foundFlightsUiState = viewModel.foundFlightsList
val foundFlightsUiState = viewModel.foundFlightsUiState
Scaffold(
topBar = {}
@ -55,7 +55,7 @@ fun FoundFlights(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),
flightList = foundFlightsUiState,
flightList = foundFlightsUiState.flightList,
onClick = { uid: Int ->
val route = BottomBarScreen.FlightInfo.passId(uid.toString())
navController.navigate(route)

View File

@ -54,7 +54,6 @@ fun Profile(
currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val getUser by remember { mutableStateOf(currentUserViewModel.user) }
val userUiState = getUser?.toUiState(true)
Log.d("CurrentUserViewModel1", "Текущий пользователь: $getUser")
val state = rememberScrollState()
LaunchedEffect(Unit) { state.animateScrollTo(100) }
@ -150,40 +149,40 @@ fun Profile(
Text("Изменить")
}
)
// Button(
// modifier = Modifier
// .fillMaxWidth()
// .padding(5.dp),
// onClick = {
// val route = BottomBarScreen.MyRents.passId(getUser?.id.toString())
// navController.navigate(route)
// },
// elevation = ButtonDefaults.buttonElevation(
// defaultElevation = 10.dp,
// pressedElevation = 6.dp
// ),
// shape = RoundedCornerShape(15.dp),
// colors = ButtonDefaults.buttonColors(containerColor = colorResource(R.color.lightBlue)),
// content = {
// Text("Мои бронирования")
// }
// )
// Button(
// modifier = Modifier
// .fillMaxWidth()
// .padding(5.dp),
// onClick = {
// navController.navigate(AuthScreen.Login.route)
// },
// elevation = ButtonDefaults.buttonElevation(
// defaultElevation = 10.dp,
// pressedElevation = 6.dp
// ),
// shape = RoundedCornerShape(15.dp),
// colors = ButtonDefaults.buttonColors(containerColor = colorResource(R.color.lightBlue)),
// content = {
// Text("Выйти")
// }
// )
Button(
modifier = Modifier
.fillMaxWidth()
.padding(5.dp),
onClick = {
val route = BottomBarScreen.MyRents.passId(getUser?.id.toString())
navController.navigate(route)
},
elevation = ButtonDefaults.buttonElevation(
defaultElevation = 10.dp,
pressedElevation = 6.dp
),
shape = RoundedCornerShape(15.dp),
colors = ButtonDefaults.buttonColors(containerColor = colorResource(R.color.lightBlue)),
content = {
Text("Мои бронирования")
}
)
Button(
modifier = Modifier
.fillMaxWidth()
.padding(5.dp),
onClick = {
navController.navigate(AuthScreen.Login.route)
},
elevation = ButtonDefaults.buttonElevation(
defaultElevation = 10.dp,
pressedElevation = 6.dp
),
shape = RoundedCornerShape(15.dp),
colors = ButtonDefaults.buttonColors(containerColor = colorResource(R.color.lightBlue)),
content = {
Text("Выйти")
}
)
}
}

View File

@ -88,11 +88,43 @@ private fun RentEdit(
.fillMaxWidth()
.padding(all = 10.dp)
) {
TextField(
value = rentUiState.rentDetails.userId.toString(),
onValueChange = { onUpdate(rentUiState.rentDetails.copy(userId = it.toInt())) },
modifier = Modifier.fillMaxWidth().padding(all = 5.dp),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.LightGray.copy(.2f),
unfocusedContainerColor = Color.LightGray.copy(.2f),
disabledContainerColor = Color.LightGray.copy(.2f),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
),
shape = RoundedCornerShape(15.dp),
label = {
Text("Номер пользователя")
}
)
TextField(
value = rentUiState.rentDetails.ticketId.toString(),
onValueChange = { onUpdate(rentUiState.rentDetails.copy(ticketId = it.toInt())) },
modifier = Modifier.fillMaxWidth().padding(all = 5.dp),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.LightGray.copy(.2f),
unfocusedContainerColor = Color.LightGray.copy(.2f),
disabledContainerColor = Color.LightGray.copy(.2f),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
),
shape = RoundedCornerShape(15.dp),
label = {
Text("Номер билета")
}
)
TextField(
value = "${userUiState.userDetails.surname} ${userUiState.userDetails.name} ${userUiState.userDetails.patronymic}",
onValueChange = {},
readOnly = true,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth().padding(all = 5.dp),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.LightGray.copy(.2f),
unfocusedContainerColor = Color.LightGray.copy(.2f),
@ -110,7 +142,7 @@ private fun RentEdit(
value = "${ticketUiState.ticketDetails.ticket_cost}",
onValueChange = {},
readOnly = true,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth().padding(all = 5.dp),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.LightGray.copy(.2f),
unfocusedContainerColor = Color.LightGray.copy(.2f),
@ -128,7 +160,7 @@ private fun RentEdit(
value = "${ticketUiState.ticketDetails.passengers_count}",
onValueChange = {},
readOnly = true,
modifier = Modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth().padding(all = 5.dp),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.LightGray.copy(.2f),
unfocusedContainerColor = Color.LightGray.copy(.2f),
@ -141,44 +173,12 @@ private fun RentEdit(
Text("Количество пассажиров")
}
)
TextField(
value = "Ожидает подтверждения",
onValueChange = { onUpdate(rentUiState.rentDetails.copy(status = it)) },
readOnly = true,
modifier = Modifier.fillMaxWidth(),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.LightGray.copy(.2f),
unfocusedContainerColor = Color.LightGray.copy(.2f),
disabledContainerColor = Color.LightGray.copy(.2f),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
),
shape = RoundedCornerShape(15.dp),
label = {
Text(stringResource(id = R.string.rent_status))
}
)
// var expanded by remember { mutableStateOf(false) }
//
// Box(
// modifier = Modifier.fillMaxWidth().padding(all = 5.dp).clip(RoundedCornerShape(15.dp))
// ) {
// ExposedDropdownMenuBox(
// modifier = Modifier.fillMaxWidth(),
// expanded = expanded,
// onExpandedChange = { expanded = !expanded },
// )
// {
// TextField(
// value = rentUiState.rentDetails.status,
// onValueChange = {},
// value = "Ожидает подтверждения",
// onValueChange = { onUpdate(rentUiState.rentDetails.copy(status = it)) },
// readOnly = true,
// trailingIcon = {
// TrailingIcon(expanded = expanded)
// },
// modifier = Modifier.menuAnchor().fillMaxWidth(),
// modifier = Modifier.fillMaxWidth(),
// colors = TextFieldDefaults.colors(
// focusedContainerColor = Color.LightGray.copy(.2f),
// unfocusedContainerColor = Color.LightGray.copy(.2f),
@ -191,41 +191,73 @@ private fun RentEdit(
// Text(stringResource(id = R.string.rent_status))
// }
// )
// ExposedDropdownMenu(
// expanded = expanded,
// onDismissRequest = { expanded = false },
// modifier = Modifier
// .background(Color.LightGray.copy(.2f))
// .exposedDropdownSize()
// .fillMaxWidth()
// ) {
// DropdownMenuItem(
// modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(15.dp)),
// text = { Text("Подтверждено") },
// onClick = {
// onUpdate(rentUiState.rentDetails.copy(status = "Подтверждено"))
// expanded = false
// }
// )
// DropdownMenuItem(
// modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(15.dp)),
// text = { Text("Ожидает подтверждения") },
// onClick = {
// onUpdate(rentUiState.rentDetails.copy(status = "Ожидает подтверждения"))
// expanded = false
// }
// )
// DropdownMenuItem(
// modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(15.dp)),
// text = { Text("Отклонено") },
// onClick = {
// onUpdate(rentUiState.rentDetails.copy(status = "Отклонено"))
// expanded = false
// }
// )
// }
// }
// }
var expanded by remember { mutableStateOf(false) }
Box(
modifier = Modifier.fillMaxWidth().padding(all = 5.dp).clip(RoundedCornerShape(15.dp))
) {
ExposedDropdownMenuBox(
modifier = Modifier.fillMaxWidth(),
expanded = expanded,
onExpandedChange = { expanded = !expanded },
)
{
TextField(
value = rentUiState.rentDetails.status,
onValueChange = {},
readOnly = true,
trailingIcon = {
TrailingIcon(expanded = expanded)
},
modifier = Modifier.menuAnchor().fillMaxWidth(),
colors = TextFieldDefaults.colors(
focusedContainerColor = Color.LightGray.copy(.2f),
unfocusedContainerColor = Color.LightGray.copy(.2f),
disabledContainerColor = Color.LightGray.copy(.2f),
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
),
shape = RoundedCornerShape(15.dp),
label = {
Text(stringResource(id = R.string.rent_status))
}
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(Color.LightGray.copy(.2f))
.exposedDropdownSize()
.fillMaxWidth()
) {
DropdownMenuItem(
modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(15.dp)),
text = { Text("Подтверждено") },
onClick = {
onUpdate(rentUiState.rentDetails.copy(status = "Подтверждено"))
expanded = false
}
)
DropdownMenuItem(
modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(15.dp)),
text = { Text("Ожидает подтверждения") },
onClick = {
onUpdate(rentUiState.rentDetails.copy(status = "Ожидает подтверждения"))
expanded = false
}
)
DropdownMenuItem(
modifier = Modifier.fillMaxWidth().clip(RoundedCornerShape(15.dp)),
text = { Text("Отклонено") },
onClick = {
onUpdate(rentUiState.rentDetails.copy(status = "Отклонено"))
expanded = false
}
)
}
}
}
Button(
enabled = rentUiState.isEntryValid,

View File

@ -68,14 +68,18 @@ import kotlinx.coroutines.launch
import ru.ulstu.`is`.airticketrentservice.database.models.Rent
import ru.ulstu.`is`.airticketrentservice.navigation.BottomBarScreen
import ru.ulstu.`is`.airticketrentservice.viewModel.AppViewModelProvider
import ru.ulstu.`is`.airticketrentservice.viewModel.CurrentUserViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.RentListViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.TicketListViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.UserListViewModel
@Composable
fun RentList(
navController: NavController,
viewModel: RentListViewModel = viewModel(factory = AppViewModelProvider.Factory),
ticketListViewModel: TicketListViewModel = viewModel(factory = AppViewModelProvider.Factory)
userListViewModel: UserListViewModel = viewModel(factory = AppViewModelProvider.Factory),
currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory),
ticketListViewModel: TicketListViewModel = viewModel(factory = AppViewModelProvider.Factory),
viewModel: RentListViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
val rentListUiState = viewModel.rentListUiState.collectAsLazyPagingItems()

View File

@ -38,13 +38,14 @@ fun TicketEdit(
flightViewModel: FlightEditViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
flightViewModel.setCurrentFlight(viewModel.ticketUiState.ticketDetails.flightId)
TicketEdit(
ticketUiState = viewModel.ticketUiState,
flightUiState = flightViewModel.flightUiState,
onClick = {
coroutineScope.launch {
viewModel.saveTicket()
navController.navigate(BottomBarScreen.RentEdit.passId(0.toString()))
navController.popBackStack()
}
},
onUpdate = viewModel::updateUiState,
@ -70,6 +71,21 @@ private fun TicketEdit(
.padding(vertical = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = ticketUiState.ticketDetails.flightId.toString(),
onValueChange = { onUpdate(ticketUiState.ticketDetails.copy(flightId = it.toInt())) },
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text("Номер рейса") },
singleLine = true
)
TextField(
modifier = Modifier
.fillMaxWidth()
@ -106,33 +122,16 @@ private fun TicketEdit(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = flightUiState.flightDetails.departure_date,
onValueChange = {},
value = ticketUiState.ticketDetails.passengers_count.toString(),
onValueChange = { onUpdate(ticketUiState.ticketDetails.copy(passengers_count = it.toInt())) },
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_arrivalDate)) },
singleLine = true,
readOnly = true
)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = flightUiState.flightDetails.arrival_date,
onValueChange = {},
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_departureDate)) },
singleLine = true,
readOnly = true
label = { Text("Количество пассажиров") },
singleLine = true
)
TextField(
modifier = Modifier
@ -150,21 +149,6 @@ private fun TicketEdit(
singleLine = true,
readOnly = true
)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = ticketUiState.ticketDetails.passengers_count.toString(),
onValueChange = { onUpdate(ticketUiState.ticketDetails.copy(passengers_count = it.toInt())) },
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text("Количество пассажиров") },
singleLine = true
)
// TextField(
// modifier = Modifier
// .fillMaxWidth()
@ -192,7 +176,7 @@ private fun TicketEdit(
colors = ButtonDefaults.buttonColors(containerColor = colorResource(R.color.lightBlue)),
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Забронировать")
Text(text = "Сохранить")
}
}
}

View File

@ -0,0 +1,244 @@
package ru.ulstu.`is`.airticketrentservice.screen
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.spring
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DismissDirection
import androidx.compose.material3.DismissState
import androidx.compose.material3.DismissValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SwipeToDismiss
import androidx.compose.material3.Text
import androidx.compose.material3.rememberDismissState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import ru.ulstu.`is`.airticketrentservice.R
import ru.ulstu.`is`.airticketrentservice.database.models.Ticket
import ru.ulstu.`is`.airticketrentservice.navigation.BottomBarScreen
import ru.ulstu.`is`.airticketrentservice.viewModel.AppViewModelProvider
import ru.ulstu.`is`.airticketrentservice.viewModel.FlightListViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.TicketListViewModel
@Composable
fun TicketList(
navController: NavController,
flightListViewModel: FlightListViewModel = viewModel(factory = AppViewModelProvider.Factory),
viewModel: TicketListViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
val ticketListUiState = viewModel.ticketListUiState.collectAsLazyPagingItems()
Scaffold(
topBar = {},
floatingActionButton = {
FloatingActionButton(
onClick = {
val route = BottomBarScreen.TicketEdit.passId(0.toString())
navController.navigate(route)
},
containerColor = colorResource(R.color.lightlightBlue)
) {
Icon(Icons.Filled.Add, "Добавить")
}
}
) { innerPadding ->
TicketList(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize(),
ticketList = ticketListUiState,
onClick = { id: Int ->
val route = BottomBarScreen.TicketEdit.passId(id.toString())
navController.navigate(route)
},
onSwipe = { ticket: Ticket ->
coroutineScope.launch {
viewModel.deleteTicket(ticket)
}
}
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun DismissBackground(dismissState: DismissState) {
val color = when (dismissState.dismissDirection) {
DismissDirection.StartToEnd -> Color.Transparent
DismissDirection.EndToStart -> Color(0xFFFF1744)
null -> Color.Transparent
}
val direction = dismissState.dismissDirection
Row(
modifier = Modifier
.fillMaxSize()
.background(color)
.padding(12.dp, 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
if (direction == DismissDirection.EndToStart) {
Icon(
Icons.Default.Delete,
contentDescription = "delete",
tint = Color.White
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SwipeToDelete(
dismissState: DismissState,
ticket: Ticket,
onClick: (id: Int) -> Unit
) {
SwipeToDismiss(
modifier = Modifier.zIndex(1f),
state = dismissState,
directions = setOf(
DismissDirection.EndToStart
),
background = {
DismissBackground(dismissState)
},
dismissContent = {
TicketListItem(ticket = ticket,
modifier = Modifier
.padding(vertical = 7.dp)
.clickable { onClick(ticket.id) })
}
)
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@Composable
private fun TicketList(
modifier: Modifier = Modifier,
ticketList: LazyPagingItems<Ticket>,
onClick: (id: Int) -> Unit,
onSwipe: (ticket: Ticket) -> Unit
) {
val refreshScope = rememberCoroutineScope()
var refreshing by remember { mutableStateOf(false) }
fun refresh() = refreshScope.launch {
refreshing = true
ticketList.refresh()
refreshing = false
}
val state = rememberPullRefreshState(refreshing, ::refresh)
Box(
modifier = modifier.pullRefresh(state)
) {
Column(
modifier = modifier.fillMaxSize()
) {
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(
count = ticketList.itemCount,
key = ticketList.itemKey(),
contentType = ticketList.itemContentType()
) { index ->
val ticket = ticketList[index]
ticket?.let {
var show by remember { mutableStateOf(true) }
val dismissState = rememberDismissState(
confirmValueChange = {
if (it == DismissValue.DismissedToStart ||
it == DismissValue.DismissedToEnd
) {
show = false
true
} else false
}, positionalThreshold = { 200.dp.toPx() }
)
AnimatedVisibility(
show, exit = fadeOut(spring())
) {
SwipeToDelete(
dismissState = dismissState,
ticket = ticket,
onClick = onClick
)
}
LaunchedEffect(show) {
if (!show) {
delay(800)
onSwipe(ticket)
}
}
}
}
}
PullRefreshIndicator(
refreshing, state,
Modifier
.align(Alignment.CenterHorizontally)
.zIndex(100f)
)
}
}
}
@Composable
private fun TicketListItem(
ticket: Ticket, modifier: Modifier = Modifier
) {
Card(
modifier = modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Column(
modifier = modifier.padding(all = 10.dp)
) {
Text(
text = "Билет ${ticket.id}, Стоимость: ${ticket.ticket_cost}"
)
}
}
}

View File

@ -0,0 +1,204 @@
package ru.ulstu.`is`.airticketrentservice.screen
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableDoubleStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import kotlinx.coroutines.launch
import ru.ulstu.`is`.airticketrentservice.R
import ru.ulstu.`is`.airticketrentservice.navigation.BottomBarScreen
import ru.ulstu.`is`.airticketrentservice.viewModel.AppViewModelProvider
import ru.ulstu.`is`.airticketrentservice.viewModel.FlightEditViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.FlightUiState
import ru.ulstu.`is`.airticketrentservice.viewModel.TicketDetails
import ru.ulstu.`is`.airticketrentservice.viewModel.TicketEditViewModel
import ru.ulstu.`is`.airticketrentservice.viewModel.TicketUiState
@Composable
fun TicketView(
navController: NavController,
viewModel: TicketEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
flightViewModel: FlightEditViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val coroutineScope = rememberCoroutineScope()
flightViewModel.setCurrentFlight(viewModel.ticketUiState.ticketDetails.flightId)
val totalCost by remember { mutableDoubleStateOf(flightViewModel.flightUiState.flightDetails.one_ticket_cost * viewModel.ticketUiState.ticketDetails.passengers_count.toDouble()) }
TicketEdit(
ticketUiState = viewModel.ticketUiState,
flightUiState = flightViewModel.flightUiState,
onClick = {
coroutineScope.launch {
viewModel.saveTicket()
navController.navigate(BottomBarScreen.RentEdit.passId(0.toString()))
}
},
onUpdate = viewModel::updateUiState,
totalCost = totalCost
)
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TicketEdit(
ticketUiState: TicketUiState,
flightUiState: FlightUiState,
onClick: () -> Unit,
onUpdate: (TicketDetails) -> Unit,
totalCost: Double
) {
//ticketUiState.ticketDetails.ticket_cost = totalCost
Column(
Modifier
.fillMaxWidth()
.padding(all = 10.dp)
.padding(horizontal = 24.dp)
.padding(vertical = 32.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = flightUiState.flightDetails.direction_from,
onValueChange = {},
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_from)) },
singleLine = true,
readOnly = true
)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = flightUiState.flightDetails.direction_to,
onValueChange = {},
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_to)) },
singleLine = true,
readOnly = true
)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = flightUiState.flightDetails.departure_date,
onValueChange = {},
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_arrivalDate)) },
singleLine = true,
readOnly = true
)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = flightUiState.flightDetails.arrival_date,
onValueChange = {},
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text(stringResource(id = R.string.ticket_departureDate)) },
singleLine = true,
readOnly = true
)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = totalCost.toString(),
onValueChange = { onUpdate(ticketUiState.ticketDetails.copy(ticket_cost = it.toDouble())) },
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text("Стоимость билета") },
singleLine = true,
readOnly = true
)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = ticketUiState.ticketDetails.passengers_count.toString(),
onValueChange = { onUpdate(ticketUiState.ticketDetails.copy(passengers_count = it.toInt())) },
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text("Количество пассажиров") },
singleLine = true
)
TextField(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
value = totalCost.toString(),
onValueChange = {},
colors = TextFieldDefaults.textFieldColors(
containerColor = Color.LightGray.copy(.2f),
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
shape = RoundedCornerShape(15.dp),
label = { Text("Стоимость за всех пассажиров") },
singleLine = true,
readOnly = true
)
Button(
onClick = onClick,
enabled = ticketUiState.isEntryValid,
elevation = ButtonDefaults.buttonElevation(
defaultElevation = 10.dp,
pressedElevation = 6.dp
),
shape = RoundedCornerShape(15.dp),
colors = ButtonDefaults.buttonColors(containerColor = colorResource(R.color.lightBlue)),
modifier = Modifier.fillMaxWidth()
) {
Text(text = "Забронировать")
}
}
}

View File

@ -51,7 +51,6 @@ fun UserEdit(
onClick = {
coroutineScope.launch {
viewModel.saveUser()
currentUserViewModel.user = viewModel.userUiState.user
navController.popBackStack()
}
},

View File

@ -6,11 +6,13 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ru.ulstu.`is`.airticketrentservice.api.AppService
import ru.ulstu.`is`.airticketrentservice.api.model.toFlight
import ru.ulstu.`is`.airticketrentservice.database.AppContainer
@ -19,30 +21,29 @@ import ru.ulstu.`is`.airticketrentservice.database.repository.FlightRepository
class FindFlightsViewModel(
savedStateHandle: SavedStateHandle,
private val flightRepository: FlightRepository,
private val flightRepository: FlightRepository
) : ViewModel() {
private val from: String = checkNotNull(savedStateHandle["direction_from"])
private val to: String = checkNotNull(savedStateHandle["direction_to"])
private val departureDate: String = checkNotNull(savedStateHandle["departure_date"])
// private val appService: AppService
// get() {
// return AppService
// }
var foundFlightsList: List<Flight> by mutableStateOf(emptyList())
private set
var foundFlightsUiState by mutableStateOf(FoundFlightsUiState())
private set
init {
viewModelScope.launch {
//foundFlightsList = appService.foundFlights().map { it.toFlight() }
foundFlightsUiState = FoundFlightsUiState(flightRepository.foundFlights(from, to, departureDate))
withContext(Dispatchers.IO) {
val flights = flightRepository.findFlights(from, to, departureDate)
launch(Dispatchers.Main) {
foundFlightsUiState = FoundFlightsUiState(flights)
}
}
}
}
}
data class FoundFlightsUiState(val flightList: List<Flight> = listOf())
// var foundFlightsList: List<Flight> by mutableStateOf(emptyList())
// private set
//foundFlightsList = appService.foundFlights().map { it.toFlight() }

View File

@ -17,6 +17,9 @@ class FlightEditViewModel(
private val flightRepository: FlightRepository
) : ViewModel() {
var flightsListUiState by mutableStateOf(FlightsListUiState())
private set
var flightUiState by mutableStateOf(FlightUiState())
private set
@ -25,16 +28,23 @@ class FlightEditViewModel(
init {
viewModelScope.launch {
if (flightUid > 0) {
flightsListUiState = FlightsListUiState(flightRepository.getAllFlights())
flightUiState = flightRepository.getFlightById(flightUid)
.toUiState(true)
}
}
}
fun setCurrentFlight(flightId: Int) {
val flight: Flight? = flightsListUiState.flightList.firstOrNull { flight -> flight.id == flightId }
flight?.let { updateUiState(it.toDetails()) }
}
fun updateUiState(flightDetails: FlightDetails) {
flightUiState = FlightUiState(
flightDetails = flightDetails,
isEntryValid = validateInput(flightDetails)
isEntryValid = validateInput(flightDetails),
flight = flightDetails.toFlight()
)
}
@ -66,7 +76,8 @@ class FlightEditViewModel(
data class FlightUiState(
val flightDetails: FlightDetails = FlightDetails(),
val isEntryValid: Boolean = false
val isEntryValid: Boolean = false,
val flight: Flight? = null
)
data class FlightDetails(
@ -102,3 +113,6 @@ fun Flight.toUiState(isEntryValid: Boolean = false): FlightUiState = FlightUiSta
isEntryValid = isEntryValid
)
data class FlightsListUiState(val flightList: List<Flight> = listOf())

View File

@ -12,28 +12,8 @@ class FlightListViewModel(
private val flightRepository: FlightRepository
): ViewModel() {
//val foundFlightsUiState: Flow<PagingData<Flight>> = flightRepository.findPagingFlights(from, to, departureDate)
var flightsListUiState: Flow<PagingData<Flight>> = flightRepository.getFlights()
// val flightListUiState: StateFlow<FlightListUiState> = flightRepository.getAllFlights().map {
// FlightListUiState(it)
// }.stateIn(
// scope = viewModelScope,
// started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppContainer.TIMEOUT),
// initialValue = FlightListUiState()
// )
// val _searchResults = MutableStateFlow<PagingData<Flight>>(PagingData.empty())
// val searchResults: MutableStateFlow<PagingData<Flight>> = _searchResults
//
// fun searchFlights(from: String, to: String, departureDate: String) {
// viewModelScope.launch {
// val results = flightRepository.findPagingFlights(from, to, departureDate).single()
// _searchResults.value = results
// }
// }
fun deleteFlight(flight: Flight) = viewModelScope.launch {
flightRepository.deleteFlight(flight)
}

View File

@ -9,6 +9,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ru.ulstu.`is`.airticketrentservice.database.models.Rent
import ru.ulstu.`is`.airticketrentservice.database.repository.RentRepository
@ -23,9 +24,15 @@ class RentEditViewModel(
init {
viewModelScope.launch {
if (rentUid > 0) {
rentUiState = rentRepository.getRentById(rentUid)
withContext(Dispatchers.IO) {
val rent = rentRepository.getRentById(rentUid)
.toUiState(true)
launch(Dispatchers.Main) {
if (rentUid > 0) {
rentUiState = rent
}
}
}
}
}

View File

@ -6,7 +6,9 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ru.ulstu.`is`.airticketrentservice.database.models.Ticket
import ru.ulstu.`is`.airticketrentservice.database.repository.TicketRepository
@ -25,30 +27,41 @@ class TicketEditViewModel(
init {
viewModelScope.launch {
ticketsListUiState = TicketsListUiState(ticketRepository.getAllTickets())
if (ticketUid > 0) {
ticketUiState = ticketRepository.getTicketById(ticketUid)
withContext(Dispatchers.IO) {
val tickets = ticketRepository.getAllTickets()
val ticket = ticketRepository.getTicketById(ticketUid)
.toUiState(true)
launch(Dispatchers.Main) {
if (ticketUid > 0) {
ticketsListUiState = TicketsListUiState(tickets)
ticketUiState = ticket
}
}
}
}
}
// init {
// viewModelScope.launch {
// if (ticketUid > 0) {
// ticketsListUiState = TicketsListUiState(ticketRepository.getAllTickets())
// ticketUiState = ticketRepository.getTicketById(ticketUid)
// .toUiState(true)
// }
// }
// }
fun setCurrentTicket(ticketId: Int) {
val ticket: Ticket? =
ticketsListUiState.ticketList.firstOrNull { ticket -> ticket.id == ticketId }
ticket?.let { updateTicketUiState(it) }
}
fun updateTicketUiState(ticket: Ticket) {
ticketUiState = TicketUiState(
ticket = ticket
)
ticket?.let { updateUiState(it.toDetails()) }
}
fun updateUiState(ticketDetails: TicketDetails) {
ticketUiState = TicketUiState(
ticketDetails = ticketDetails,
isEntryValid = validateInput(ticketDetails)
isEntryValid = validateInput(ticketDetails),
ticket = ticketDetails.toTicket()
)
}
@ -84,7 +97,7 @@ data class TicketUiState(
data class TicketDetails(
val passengers_count: Int = 0,
var ticket_cost: Double = 0.0,
var flightId: Int = 0,
var flightId: Int = 0
)
fun TicketDetails.toTicket(uid: Int = 0): Ticket = Ticket(

View File

@ -10,23 +10,11 @@ import kotlinx.coroutines.launch
import ru.ulstu.`is`.airticketrentservice.database.models.Ticket
import ru.ulstu.`is`.airticketrentservice.database.repository.TicketRepository
class TicketListViewModel(private val ticketRepository: TicketRepository): ViewModel() {
val ticketListUiState: Flow<PagingData<Ticket>> = ticketRepository.getTickets()
class TicketListViewModel(
private val ticketRepository: TicketRepository
): ViewModel() {
// private var userId: Int = 0
// fun setUserId(userId: Int) {
// this.userId = userId
// }
// var userRentsUiState by mutableStateOf(UserRentsUiState())
// private set
//
// init {
// viewModelScope.launch {
// if (userId > 0) {
// userRentsUiState = UserRentsUiState(userRepository.getUserRents(userId))
// }
// }
// }
val ticketListUiState: Flow<PagingData<Ticket>> = ticketRepository.getTickets()
suspend fun deleteTicket(ticket: Ticket) {
ticketRepository.deleteTicket(ticket)

View File

@ -27,8 +27,8 @@ class UserEditViewModel(
init {
viewModelScope.launch {
usersListUiState = UsersListUiState(userRepository.getAllUsers())
if (userUid > 0) {
usersListUiState = UsersListUiState(userRepository.getAllUsers())
userUiState = userRepository.getUserById(userUid)
.toUiState(true)!!
}
@ -38,19 +38,14 @@ class UserEditViewModel(
fun setCurrentUser(userId: Int) {
val user: User? =
usersListUiState.userList.firstOrNull { user -> user.id == userId }
user?.let { updateUserUiState(it) }
}
fun updateUserUiState(user: User) {
userUiState = UserUiState(
user = user
)
user?.let { it.toDetails()?.let { it1 -> updateUiState(it1) } }
}
fun updateUiState(userDetails: UserDetails) {
userUiState = UserUiState(
userDetails = userDetails,
isEntryValid = validateInput(userDetails)
isEntryValid = validateInput(userDetails),
user = userDetails.toUser()
)
}

View File

@ -6,7 +6,9 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import ru.ulstu.`is`.airticketrentservice.database.models.Rent
import ru.ulstu.`is`.airticketrentservice.database.repository.RentRepository
import ru.ulstu.`is`.airticketrentservice.database.repository.UserRepository
@ -24,8 +26,13 @@ class UsersRentsViewModel(
init {
viewModelScope.launch {
withContext(Dispatchers.IO) {
val rents = userRepository.getUserRents(userUid)
launch(Dispatchers.Main) {
if (userUid > 0) {
userRentsUiState = UserRentsUiState(userRepository.getUserRents(userUid))
userRentsUiState = UserRentsUiState(rents)
}
}
}
}
}

View File

@ -3,11 +3,17 @@
{
"id": 1,
"passengers_count": 1,
"ticket_cost": 1000,
"flightId": 2
"ticket_cost": 2680,
"flightId": 6
},
{
"id": 2,
"passengers_count": 1,
"ticket_cost": 2435,
"flightId": 5
}
],
"searchFlights": [
"flights": [
{
"id": 2,
"direction_from": "a",
@ -18,7 +24,7 @@
"one_ticket_cost": 1000
},
{
"direction_from": "Санкт-Петербург ",
"direction_from": "Санкт-Петербург",
"direction_to": "Сочи",
"departure_date": "17-12-2023",
"arrival_date": "17-12-2023",
@ -28,17 +34,15 @@
},
{
"id": 6,
"direction_from": "Ульяновск ",
"direction_from": "Ульяновск",
"direction_to": "Москва ",
"departure_date": "26-12-2023",
"arrival_date": "26-12-2023",
"tickets_count": 50,
"one_ticket_cost": 2680
}
],
"flights": [
},
{
"direction_from": "Хабаровск ",
"direction_from": "Хабаровск",
"direction_to": "Кострома ",
"departure_date": "5-1-2024",
"arrival_date": "5-1-2024",
@ -47,7 +51,7 @@
"id": 7
},
{
"direction_from": "Астрахань ",
"direction_from": "Астрахань",
"direction_to": "Нижний Новгород",
"departure_date": "28-1-2024",
"arrival_date": "28-1-2024",
@ -79,7 +83,7 @@
"id": 2,
"surname": "Артамонова",
"name": "Татьяна",
"patronymic": "Валерьевна",
"patronymic": "Валерьевн",
"date_of_birth": "7-11-2003",
"email": "usertt@mail.ru",
"password": "usertt",
@ -142,6 +146,12 @@
"status": "Ожидает подтверждения",
"userId": 2,
"ticketId": 1
},
{
"id": 2,
"status": "Подтверждено",
"userId": 2,
"ticketId": 2
}
]
}

View File

@ -3,11 +3,17 @@
{
"id": 1,
"passengers_count": 1,
"ticket_cost": 1000,
"flightId": 2
"ticket_cost": 2680,
"flightId": 6
},
{
"id": 2,
"passengers_count": 1,
"ticket_cost": 2435,
"flightId": 5
}
],
"foundFlights": [
"flights": [
{
"id": 2,
"direction_from": "a",
@ -18,7 +24,7 @@
"one_ticket_cost": 1000
},
{
"direction_from": "Санкт-Петербург ",
"direction_from": "Санкт-Петербург",
"direction_to": "Сочи",
"departure_date": "17-12-2023",
"arrival_date": "17-12-2023",
@ -28,17 +34,15 @@
},
{
"id": 6,
"direction_from": "Ульяновск ",
"direction_from": "Ульяновск",
"direction_to": "Москва ",
"departure_date": "26-12-2023",
"arrival_date": "26-12-2023",
"tickets_count": 50,
"one_ticket_cost": 2680
}
],
"flights": [
},
{
"direction_from": "Хабаровск ",
"direction_from": "Хабаровск",
"direction_to": "Кострома ",
"departure_date": "5-1-2024",
"arrival_date": "5-1-2024",
@ -47,7 +51,7 @@
"id": 7
},
{
"direction_from": "Астрахань ",
"direction_from": "Астрахань",
"direction_to": "Нижний Новгород",
"departure_date": "28-1-2024",
"arrival_date": "28-1-2024",
@ -142,6 +146,12 @@
"status": "Ожидает подтверждения",
"userId": 2,
"ticketId": 1
},
{
"id": 2,
"status": "Подтверждено",
"userId": 2,
"ticketId": 2
}
]
}