отчет
This commit is contained in:
parent
d45b6d7ed8
commit
aab18402b5
@ -2,10 +2,14 @@ package com.example.myapplication.api
|
||||
|
||||
import com.example.myapplication.api.cinema.CinemaRemote
|
||||
import com.example.myapplication.api.order.OrderRemote
|
||||
import com.example.myapplication.api.cinema.CinemaWithSessionsRemote
|
||||
import com.example.myapplication.api.session.ReportRemote
|
||||
import com.example.myapplication.api.session.SessionFromCinemaRemote
|
||||
import com.example.myapplication.api.session.SessionRemote
|
||||
import com.example.myapplication.api.session.SessionWithCinemaRemote
|
||||
import com.example.myapplication.api.user.UserRemote
|
||||
import com.example.myapplication.api.user.UserSessionRemote
|
||||
import com.example.myapplication.api.user.UserSessionWithSessionRemote
|
||||
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.modules.SerializersModule
|
||||
@ -21,9 +25,10 @@ import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
import java.util.Date
|
||||
|
||||
interface MyServerService {
|
||||
/*@GET("orders")
|
||||
@GET("orders")
|
||||
suspend fun getOrders(): List<OrderRemote>
|
||||
|
||||
@GET("users")
|
||||
@ -40,6 +45,11 @@ interface MyServerService {
|
||||
@Path("id") id: Int,
|
||||
): CinemaRemote
|
||||
|
||||
@GET("cinemas/{id}?_embed=sessions")
|
||||
suspend fun getCinemaWithSessions(
|
||||
@Path("id") id: Int,
|
||||
): CinemaWithSessionsRemote
|
||||
|
||||
@POST("cinemas")
|
||||
suspend fun createCinema(
|
||||
@Body cinema: CinemaRemote,
|
||||
@ -56,11 +66,6 @@ interface MyServerService {
|
||||
@Path("id") id: Int,
|
||||
)
|
||||
|
||||
@GET("cinemas/{cinemaId}/sessions")
|
||||
suspend fun getSessionsForCinema(
|
||||
@Path("cinemaId") cinemaId: Int
|
||||
): List<SessionFromCinemaRemote>
|
||||
|
||||
@GET("sessions/{id}?_expand=cinema")
|
||||
suspend fun getSession(
|
||||
@Path("id") id: Int,
|
||||
@ -82,21 +87,40 @@ interface MyServerService {
|
||||
@Path("id") id: Int,
|
||||
): SessionFromCinemaRemote
|
||||
|
||||
@GET("users/{id}")
|
||||
@GET("userssessions?_expand=session")
|
||||
suspend fun getUserCart(
|
||||
@Query("userId") userId: Int,
|
||||
): List<UserSessionWithSessionRemote>
|
||||
|
||||
@GET("userssessions?_expand=session")
|
||||
suspend fun getUsersSessions(): List<UserSessionWithSessionRemote>
|
||||
|
||||
@DELETE("userssessions/{id}")
|
||||
suspend fun deleteUserSession(
|
||||
@Path("id") id: Int,
|
||||
): UserRemote
|
||||
): UserSessionRemote
|
||||
|
||||
@GET("users?_limit=1")
|
||||
suspend fun getUser(
|
||||
@Query("login") login: String,
|
||||
): List<UserRemote>
|
||||
|
||||
@PUT("users/{id}")
|
||||
@GET("userssessions?_limit=1")
|
||||
suspend fun getUserSession(
|
||||
@Query("userId") userId: Int,
|
||||
@Query("sessionId") sessionId: Int,
|
||||
): List<UserSessionRemote>
|
||||
|
||||
@POST("userssessions")
|
||||
suspend fun createUserSession(
|
||||
@Body userSessionRemote: UserSessionRemote,
|
||||
): UserSessionRemote
|
||||
|
||||
@PUT("userssessions/{id}")
|
||||
suspend fun updateUserCart(
|
||||
@Path("id") id: Int,
|
||||
@Body userRemote: UserRemote,
|
||||
): UserRemote
|
||||
@Body userSessionRemote: UserSessionRemote,
|
||||
): UserSessionRemote
|
||||
|
||||
@POST("users")
|
||||
suspend fun createUser(
|
||||
@ -105,6 +129,7 @@ interface MyServerService {
|
||||
|
||||
@GET("orders")
|
||||
suspend fun getOrders(
|
||||
@Query("userId") userId: Int,
|
||||
@Query("_page") page: Int,
|
||||
@Query("_limit") limit: Int,
|
||||
): List<OrderRemote>
|
||||
@ -123,11 +148,17 @@ interface MyServerService {
|
||||
suspend fun updateOrder(
|
||||
@Path("id") id: Int,
|
||||
@Body orderRemote: OrderRemote,
|
||||
): OrderRemote*/
|
||||
): OrderRemote
|
||||
|
||||
@GET("report")
|
||||
suspend fun getReport(
|
||||
@Query("startDate") startDate: Date,
|
||||
@Query("endDate") endDate: Date
|
||||
): List<ReportRemote>
|
||||
|
||||
companion object {
|
||||
//private const val BASE_URL = "http://192.168.154.166:8080/"
|
||||
private const val BASE_URL = "http://192.168.0.101:8080/"
|
||||
private const val BASE_URL = "http://192.168.0.101:8079/"
|
||||
|
||||
@Volatile
|
||||
private var INSTANCE: MyServerService? = null
|
||||
|
@ -1,12 +1,12 @@
|
||||
package com.example.myapplication.api.cinema
|
||||
|
||||
import android.database.sqlite.SQLiteConstraintException
|
||||
import androidx.paging.ExperimentalPagingApi
|
||||
import androidx.paging.LoadType
|
||||
import androidx.paging.PagingState
|
||||
import androidx.paging.RemoteMediator
|
||||
import androidx.room.withTransaction
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.api.session.toSession
|
||||
import com.example.myapplication.database.AppDatabase
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.repository.OfflineCinemaRepository
|
||||
@ -55,10 +55,8 @@ class CinemaRemoteMediator(
|
||||
|
||||
try {
|
||||
val cinemas = service.getCinemas(page, state.config.pageSize).map { it.toCinema() }
|
||||
val cinemasWithSessions = cinemas.map { cinema ->
|
||||
service.getSessionsForCinema(cinema.uid).map {
|
||||
service.getSession(it.id).toSession()
|
||||
}
|
||||
val sessionsFromCinemas = cinemas.map { cinema ->
|
||||
service.getCinemaWithSessions(cinema.uid).toSessions()
|
||||
}
|
||||
val endOfPaginationReached = cinemas.isEmpty()
|
||||
database.withTransaction {
|
||||
@ -79,10 +77,11 @@ class CinemaRemoteMediator(
|
||||
}
|
||||
dbRemoteKeyRepository.createRemoteKeys(keys)
|
||||
dbCinemaRepository.insertCinemas(cinemas)
|
||||
cinemasWithSessions.forEach {
|
||||
sessionsFromCinemas.forEach {
|
||||
try {
|
||||
dbSessionRepository.insertSessions(it)
|
||||
} catch (_: Exception) {
|
||||
dbSessionRepository.insertSessions(it)
|
||||
} catch(_:Exception) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,6 +90,8 @@ class CinemaRemoteMediator(
|
||||
return MediatorResult.Error(exception)
|
||||
} catch (exception: HttpException) {
|
||||
return MediatorResult.Error(exception)
|
||||
} catch (exception: SQLiteConstraintException) {
|
||||
return MediatorResult.Error(exception)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,31 @@
|
||||
package com.example.myapplication.api.cinema
|
||||
|
||||
import com.example.myapplication.api.session.SessionFromCinemaRemote
|
||||
import com.example.myapplication.api.session.toSessionFromCinema
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import com.example.myapplication.database.entities.model.toSession
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CinemaWithSessionsRemote(
|
||||
val id: Int = 0,
|
||||
val name: String = "",
|
||||
val description: String = "",
|
||||
val image: ByteArray? = null,
|
||||
val year: Long = 0,
|
||||
@SerialName("sessions")
|
||||
val sessions: List<SessionFromCinemaRemote>,
|
||||
)
|
||||
|
||||
fun CinemaWithSessionsRemote.toCinema(): Cinema = Cinema(
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
image,
|
||||
year
|
||||
)
|
||||
|
||||
fun CinemaWithSessionsRemote.toSessions(): List<Session> =
|
||||
sessions.map { it.toSessionFromCinema().toSession() }
|
@ -47,20 +47,25 @@ class RestCinemaRepository(
|
||||
}
|
||||
|
||||
override suspend fun getCinema(uid: Int): CinemaWithSessions {
|
||||
val cinema = service.getCinema(uid).toCinema()
|
||||
val cinemaWithSessions = service.getCinemaWithSessions(uid)
|
||||
|
||||
val sessions = service.getSessionsForCinema(uid).map { x ->
|
||||
val sessions = cinemaWithSessions.sessions.map { sessionFromCinemaRemote ->
|
||||
SessionFromCinema(
|
||||
x.id,
|
||||
x.dateTime,
|
||||
x.price,
|
||||
x.maxCount - service.getOrders().flatMap { order ->
|
||||
order.sessions.filter { session -> session.id == x.id }
|
||||
sessionFromCinemaRemote.id,
|
||||
sessionFromCinemaRemote.dateTime,
|
||||
sessionFromCinemaRemote.price,
|
||||
sessionFromCinemaRemote.maxCount - service.getOrders().flatMap
|
||||
{ order ->
|
||||
order.sessions.filter { session ->
|
||||
session.id == sessionFromCinemaRemote.id &&
|
||||
session.cinemaId == sessionFromCinemaRemote.cinemaId &&
|
||||
session.cinema.name == cinemaWithSessions.name
|
||||
}
|
||||
}.sumOf { session -> session.count },
|
||||
uid
|
||||
)
|
||||
}
|
||||
return CinemaWithSessions(cinema, sessions)
|
||||
return CinemaWithSessions(cinemaWithSessions.toCinema(), sessions)
|
||||
}
|
||||
|
||||
override suspend fun insertCinema(cinema: Cinema) {
|
||||
@ -72,15 +77,11 @@ class RestCinemaRepository(
|
||||
}
|
||||
|
||||
override suspend fun deleteCinema(cinema: Cinema) {
|
||||
val cart = service.getUsers()
|
||||
cart.forEach { userRemote ->
|
||||
userRemote.sessions = userRemote.sessions.filter { x -> x.cinemaId != cinema.uid }
|
||||
service.updateUserCart(userRemote.id, userRemote)
|
||||
}
|
||||
val orders = service.getOrders()
|
||||
orders.forEach { orderRemote ->
|
||||
orderRemote.sessions = orderRemote.sessions.filter { x -> x.cinemaId != cinema.uid }
|
||||
service.updateOrder(orderRemote.id, orderRemote)
|
||||
val cart = service.getUsersSessions()
|
||||
cart.forEach { userSessionRemote ->
|
||||
if (userSessionRemote.session.cinemaId == cinema.uid) {
|
||||
service.deleteUserSession(userSessionRemote.id)
|
||||
}
|
||||
}
|
||||
service.deleteCinema(cinema.uid)
|
||||
dbCinemaRepository.deleteCinema(cinema)
|
||||
|
@ -5,6 +5,7 @@ import androidx.paging.LoadType
|
||||
import androidx.paging.PagingState
|
||||
import androidx.paging.RemoteMediator
|
||||
import androidx.room.withTransaction
|
||||
import com.example.myapplication.LiveStore
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.database.AppDatabase
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
@ -51,7 +52,8 @@ class OrderRemoteMediator(
|
||||
}
|
||||
|
||||
try {
|
||||
val orders = service.getOrders(page, state.config.pageSize).map { it.toOrder() }
|
||||
val orders = service.getOrders(LiveStore.user.value?.uid ?: 0,
|
||||
page, state.config.pageSize).map { it.toOrder() }
|
||||
val endOfPaginationReached = orders.isEmpty()
|
||||
database.withTransaction {
|
||||
if (loadType == LoadType.REFRESH) {
|
||||
|
@ -63,7 +63,7 @@ class RestOrderRepository(
|
||||
)
|
||||
)
|
||||
}
|
||||
return order.sessions.map { x -> x.toSessionFromOrder(dbCinemaRepository.getCinema(x.cinemaId).cinema.toCinemaRemote()) }
|
||||
return order.sessions.map { x -> x.toSessionFromOrder() }
|
||||
}
|
||||
|
||||
override suspend fun insertOrder(order: Order): Long {
|
||||
|
@ -13,14 +13,15 @@ class RestOrderSessionRepository(
|
||||
) : OrderSessionRepository {
|
||||
override suspend fun insertOrderSession(orderSessionCrossRef: OrderSessionCrossRef) {
|
||||
var orderRemote = service.getOrder(orderSessionCrossRef.orderId)
|
||||
val session = service.getSession(orderSessionCrossRef.sessionId).toSession()
|
||||
val session = service.getSession(orderSessionCrossRef.sessionId)
|
||||
|
||||
val sessionFromOrder = SessionFromOrderRemote(
|
||||
session.uid,
|
||||
session.id,
|
||||
session.dateTime,
|
||||
session.price,
|
||||
orderSessionCrossRef.count,
|
||||
session.cinemaId
|
||||
session.cinemaId,
|
||||
session.cinema
|
||||
)
|
||||
|
||||
val updatedSessions = orderRemote.sessions.toMutableList()
|
||||
|
@ -1,71 +0,0 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import com.example.myapplication.api.cinema.CinemaRemote
|
||||
import com.example.myapplication.api.cinema.toCinema
|
||||
import com.example.myapplication.api.session.SessionFromCinemaRemote
|
||||
import com.example.myapplication.api.session.toSessionFromCinema
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.model.CinemaWithSessions
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
/*
|
||||
@Serializable
|
||||
data class CinemaWithSessionsRemote(
|
||||
val id: Int = 0,
|
||||
val name: String = "",
|
||||
val description: String = "",
|
||||
val image: ByteArray? = null,
|
||||
val year: Long = 0,
|
||||
@SerialName("sessions")
|
||||
val sessions: List<SessionFromCinemaRemote>,
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as CinemaWithSessionsRemote
|
||||
|
||||
if (id != other.id) return false
|
||||
if (name != other.name) return false
|
||||
if (description != other.description) return false
|
||||
if (image != null) {
|
||||
if (other.image == null) return false
|
||||
if (!image.contentEquals(other.image)) return false
|
||||
} else if (other.image != null) return false
|
||||
if (year != other.year) return false
|
||||
if (sessions != other.sessions) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = id
|
||||
result = 31 * result + name.hashCode()
|
||||
result = 31 * result + description.hashCode()
|
||||
result = 31 * result + (image?.contentHashCode() ?: 0)
|
||||
result = 31 * result + year.hashCode()
|
||||
result = 31 * result + sessions.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
fun CinemaWithSessionsRemote.toCinemaWithSessions(): CinemaWithSessions = CinemaWithSessions(
|
||||
Cinema(
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
image,
|
||||
year
|
||||
),
|
||||
sessions.map { x -> x.toSessionFromCinema() }
|
||||
)
|
||||
|
||||
fun Cinema.toCinemaWithSessionsRemote(): CinemaWithSessionsRemote = CinemaWithSessionsRemote(
|
||||
uid,
|
||||
name,
|
||||
description,
|
||||
image,
|
||||
year,
|
||||
sessions = emptyList()
|
||||
)*/
|
@ -0,0 +1,21 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReportRemote(
|
||||
@SerialName("cinema_name")
|
||||
val cinemaName: String = "",
|
||||
@Contextual
|
||||
@SerialName("current_ticket_date_time")
|
||||
val ticketDateTime: org.threeten.bp.LocalDateTime,
|
||||
@SerialName("current_ticket_price")
|
||||
val ticketPrice: Double = 0.0,
|
||||
@SerialName("max_ticket_quantity")
|
||||
val ticketQuantity: Int = 0,
|
||||
@SerialName("purchased_tickets")
|
||||
val ticketsPurchased: Int = 0,
|
||||
val revenue: Double = 0.0
|
||||
)
|
@ -6,6 +6,7 @@ import com.example.myapplication.database.entities.repository.OfflineOrderSessio
|
||||
import com.example.myapplication.database.entities.repository.OfflineSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.SessionRepository
|
||||
import java.util.Date
|
||||
|
||||
class RestSessionRepository(
|
||||
private val service: MyServerService,
|
||||
@ -33,10 +34,11 @@ class RestSessionRepository(
|
||||
}
|
||||
|
||||
override suspend fun deleteSession(session: Session) {
|
||||
val cart = service.getUsers()
|
||||
cart.forEach { userRemote ->
|
||||
userRemote.sessions = userRemote.sessions.filter { x -> x.id != session.uid }
|
||||
service.updateUserCart(userRemote.id, userRemote)
|
||||
val cart = service.getUsersSessions()
|
||||
cart.forEach { userSessionRemote ->
|
||||
if (userSessionRemote.session.id == session.uid) {
|
||||
service.deleteUserSession(userSessionRemote.id)
|
||||
}
|
||||
}
|
||||
val orders = service.getOrders()
|
||||
orders.forEach { orderRemote ->
|
||||
@ -48,4 +50,8 @@ class RestSessionRepository(
|
||||
dbOrderSessionRepository.deleteSessionsByUid(session.uid)
|
||||
dbSessionRepository.deleteSession(session)
|
||||
}
|
||||
|
||||
suspend fun getReport(startDate: Date, endDate: Date): List<ReportRemote> {
|
||||
return service.getReport(startDate, endDate)
|
||||
}
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package com.example.myapplication.api.session
|
||||
|
||||
import com.example.myapplication.database.entities.model.Session
|
||||
import com.example.myapplication.database.entities.model.SessionFromCinema
|
||||
import kotlinx.serialization.Contextual
|
||||
import kotlinx.serialization.Serializable
|
||||
@ -24,11 +23,3 @@ fun SessionFromCinemaRemote.toSessionFromCinema(): SessionFromCinema = SessionFr
|
||||
availableCount,
|
||||
cinemaId
|
||||
)
|
||||
|
||||
fun SessionFromCinema.toSessionFromCinemaRemote(): SessionFromCinemaRemote = SessionFromCinemaRemote(
|
||||
uid,
|
||||
dateTime,
|
||||
price,
|
||||
availableCount,
|
||||
cinemaId
|
||||
)
|
@ -14,9 +14,10 @@ class SessionFromOrderRemote(
|
||||
val frozenPrice: Double = 0.0,
|
||||
val count: Int = 0,
|
||||
val cinemaId: Int = 0,
|
||||
val cinema: CinemaRemote,
|
||||
)
|
||||
|
||||
fun SessionFromOrderRemote.toSessionFromOrder(cinema: CinemaRemote): SessionFromOrder =
|
||||
fun SessionFromOrderRemote.toSessionFromOrder(): SessionFromOrder =
|
||||
SessionFromOrder(
|
||||
id, dateTime, frozenPrice, count, cinemaId, cinema.toCinema()
|
||||
)
|
@ -30,25 +30,21 @@ class RestUserRepository(
|
||||
override suspend fun getCartByUser(userId: Int): List<SessionFromCart> {
|
||||
val cart = service.getUserCart(userId)
|
||||
dbUserSessionRepository.deleteUserSessions(userId)
|
||||
cart.sessions.map { sessionFromCartRemote ->
|
||||
cart.map { sessionFromCartRemote ->
|
||||
dbUserSessionRepository.insertUserSession(
|
||||
UserSessionCrossRef(
|
||||
userId,
|
||||
sessionFromCartRemote.id,
|
||||
sessionFromCartRemote.sessionId,
|
||||
sessionFromCartRemote.count
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return cart.sessions.map {
|
||||
val session = service.getSession(it.id)
|
||||
return cart.map {
|
||||
val cinema = service.getCinema(it.session.cinemaId)
|
||||
it.toSessionFromCart(
|
||||
session.cinema,
|
||||
session.dateTime,
|
||||
session.price,
|
||||
session.maxCount - service.getOrders().flatMap { order ->
|
||||
it.session.maxCount - service.getOrders().flatMap { order ->
|
||||
order.sessions.filter { session -> session.id == it.id }
|
||||
}.sumOf { session -> session.count })
|
||||
}.sumOf { session -> session.count }, cinema.toCinema())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@ data class UserRemote(
|
||||
val login: String = "",
|
||||
val password: String = "",
|
||||
val role: Int = -1,
|
||||
var sessions: List<SessionFromCartRemote> = emptyList()
|
||||
)
|
||||
|
||||
fun User.toUserRemote(): UserRemote = UserRemote(
|
||||
|
@ -0,0 +1,15 @@
|
||||
package com.example.myapplication.api.user
|
||||
|
||||
import com.example.myapplication.api.session.SessionRemote
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.model.SessionFromCart
|
||||
import com.example.myapplication.database.entities.model.User
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UserSessionRemote (
|
||||
val id: Int = 0,
|
||||
val userId: Int = 0,
|
||||
val sessionId: Int = 0,
|
||||
var count: Int = 0,
|
||||
)
|
@ -0,0 +1,25 @@
|
||||
package com.example.myapplication.api.user
|
||||
|
||||
import com.example.myapplication.api.session.SessionRemote
|
||||
import com.example.myapplication.database.entities.model.Cinema
|
||||
import com.example.myapplication.database.entities.model.SessionFromCart
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class UserSessionWithSessionRemote (
|
||||
val id: Int = 0,
|
||||
val userId: Int = 0,
|
||||
val sessionId: Int = 0,
|
||||
val count: Int = 0,
|
||||
val session: SessionRemote,
|
||||
)
|
||||
|
||||
fun UserSessionWithSessionRemote.toSessionFromCart(availableCount: Int = 0, cinema: Cinema): SessionFromCart = SessionFromCart(
|
||||
sessionId,
|
||||
session.dateTime,
|
||||
session.price,
|
||||
availableCount,
|
||||
count,
|
||||
session.cinemaId,
|
||||
cinema
|
||||
)
|
@ -3,6 +3,7 @@ package com.example.myapplication.api.usersession
|
||||
import com.example.myapplication.api.MyServerService
|
||||
import com.example.myapplication.api.session.SessionFromCartRemote
|
||||
import com.example.myapplication.api.session.toSession
|
||||
import com.example.myapplication.api.user.UserSessionRemote
|
||||
import com.example.myapplication.database.entities.model.UserSessionCrossRef
|
||||
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
||||
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
||||
@ -12,51 +13,44 @@ class RestUserSessionRepository(
|
||||
private val dbUserSessionRepository: OfflineUserSessionRepository
|
||||
) : UserSessionRepository {
|
||||
override suspend fun insertUserSession(userSessionCrossRef: UserSessionCrossRef) {
|
||||
var cartSessions = service.getUserCart(userSessionCrossRef.userId)
|
||||
cartSessions.sessions.forEach { session ->
|
||||
if (session.id == userSessionCrossRef.sessionId)
|
||||
val cartSessions = service.getUserCart(userSessionCrossRef.userId)
|
||||
cartSessions.forEach { session ->
|
||||
if (session.sessionId == userSessionCrossRef.sessionId)
|
||||
return
|
||||
}
|
||||
val session = service.getSession(userSessionCrossRef.sessionId).toSession()
|
||||
|
||||
val sessionFromCart = SessionFromCartRemote(
|
||||
session.uid,
|
||||
userSessionCrossRef.count,
|
||||
session.cinemaId,
|
||||
)
|
||||
|
||||
val updatedSessions = cartSessions.sessions.toMutableList()
|
||||
updatedSessions.add(sessionFromCart)
|
||||
|
||||
cartSessions = cartSessions.copy(sessions = updatedSessions)
|
||||
service.updateUserCart(userSessionCrossRef.userId, cartSessions)
|
||||
service.createUserSession(UserSessionRemote(id = 0,
|
||||
userId = userSessionCrossRef.userId,
|
||||
sessionId = userSessionCrossRef.sessionId,
|
||||
count = userSessionCrossRef.count
|
||||
))
|
||||
dbUserSessionRepository.insertUserSession(userSessionCrossRef)
|
||||
}
|
||||
|
||||
override suspend fun updateUserSession(userSessionCrossRef: UserSessionCrossRef) {
|
||||
val userRemote = service.getUserCart(userSessionCrossRef.userId)
|
||||
val userSessionRemote = service.getUserSession(userSessionCrossRef.userId,
|
||||
userSessionCrossRef.sessionId).first()
|
||||
if (userSessionCrossRef.count <= 0) {
|
||||
userRemote.sessions =
|
||||
userRemote.sessions.filter { x -> x.id != userSessionCrossRef.sessionId }
|
||||
} else
|
||||
userRemote.sessions.forEach {
|
||||
if (it.id == userSessionCrossRef.sessionId) {
|
||||
it.count = userSessionCrossRef.count
|
||||
}
|
||||
}
|
||||
service.updateUserCart(userSessionCrossRef.userId, userRemote)
|
||||
service.deleteUserSession(userSessionRemote.id)
|
||||
dbUserSessionRepository.deleteUserSession(userSessionCrossRef)
|
||||
return
|
||||
}
|
||||
userSessionRemote.count = userSessionCrossRef.count
|
||||
service.updateUserCart(userSessionRemote.id, userSessionRemote)
|
||||
dbUserSessionRepository.updateUserSession(userSessionCrossRef)
|
||||
}
|
||||
|
||||
override suspend fun deleteUserSession(userSessionCrossRef: UserSessionCrossRef) {
|
||||
updateUserSession(userSessionCrossRef)
|
||||
val userSessionRemote = service.getUserSession(userSessionCrossRef.userId,
|
||||
userSessionCrossRef.sessionId).first()
|
||||
service.deleteUserSession(userSessionRemote.id)
|
||||
dbUserSessionRepository.deleteUserSession(userSessionCrossRef)
|
||||
}
|
||||
|
||||
override suspend fun deleteUserSessions(userId: Int) {
|
||||
val userRemote = service.getUserCart(userId)
|
||||
userRemote.sessions = emptyList()
|
||||
service.updateUserCart(userId, userRemote)
|
||||
val cart = service.getUserCart(userId)
|
||||
cart.forEach {
|
||||
service.deleteUserSession(it.id)
|
||||
}
|
||||
dbUserSessionRepository.deleteUserSessions(userId)
|
||||
}
|
||||
}
|
@ -15,7 +15,6 @@ fun Authenticator(
|
||||
dataStoreManager: DataStoreManager,
|
||||
viewModel: AuthenticatorViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val login = dataStoreManager.getLogin().collectAsState(initial = "").value
|
||||
|
@ -164,7 +164,7 @@ private fun SessionListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
onChangeCount: (SessionFromCart, Int) -> Unit,
|
||||
) {
|
||||
var currentCount by remember { mutableIntStateOf(session.count) }
|
||||
//var currentCount by remember { mutableIntStateOf(session.count) }
|
||||
|
||||
val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
|
||||
val formattedDate = dateFormatter.format(session.dateTime)
|
||||
@ -203,7 +203,7 @@ private fun SessionListItem(
|
||||
Text(
|
||||
text = "${session.cinema.name}, ${session.cinema.year}\n" +
|
||||
"Цена: ${session.price}\n" +
|
||||
"${currentCount}/${session.availableCount}",
|
||||
"${session.count}/${session.availableCount}",
|
||||
color = MaterialTheme.colorScheme.onSecondary
|
||||
)
|
||||
}
|
||||
@ -219,7 +219,7 @@ private fun SessionListItem(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
IconButton(
|
||||
onClick = { onChangeCount(session, --currentCount) }
|
||||
onClick = { onChangeCount(session, session.count - 1) }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = ImageVector.vectorResource(id = R.drawable.minus),
|
||||
@ -230,7 +230,7 @@ private fun SessionListItem(
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "$currentCount",
|
||||
text = "${session.count}",
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
|
||||
@ -238,7 +238,7 @@ private fun SessionListItem(
|
||||
onClick = {
|
||||
onChangeCount(
|
||||
session,
|
||||
if (currentCount != session.availableCount) ++currentCount else currentCount
|
||||
if (session.count != session.availableCount) session.count + 1 else session.count
|
||||
)
|
||||
}
|
||||
) {
|
||||
|
@ -15,6 +15,7 @@ import com.example.myapplication.database.entities.repository.OrderSessionReposi
|
||||
import com.example.myapplication.database.entities.repository.UserRepository
|
||||
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
||||
import org.threeten.bp.LocalDateTime
|
||||
import java.util.LinkedList
|
||||
|
||||
class CartViewModel(
|
||||
private val userSessionRepository: UserSessionRepository,
|
||||
@ -22,6 +23,19 @@ class CartViewModel(
|
||||
private val orderSessionRepository: OrderSessionRepository,
|
||||
private val userRepository: UserRepository,
|
||||
) : ViewModel() {
|
||||
private val requestQueue: LinkedList<suspend () -> Unit> = LinkedList()
|
||||
private var isProcessingQueue: Boolean = false
|
||||
|
||||
private suspend fun processQueue() {
|
||||
isProcessingQueue = true
|
||||
while (requestQueue.isNotEmpty()) {
|
||||
val request = requestQueue.poll()
|
||||
request.invoke()
|
||||
refreshState()
|
||||
}
|
||||
isProcessingQueue = false
|
||||
}
|
||||
|
||||
var isLoading: Boolean = false
|
||||
var cartUiState by mutableStateOf(CartUiState())
|
||||
private set
|
||||
@ -71,6 +85,7 @@ class CartViewModel(
|
||||
isLoading = true
|
||||
val userId: Int = LiveStore.user.value?.uid ?: return false
|
||||
if (count == 0) {
|
||||
isLoading = false
|
||||
removeFromCart(session, count)
|
||||
return false
|
||||
}
|
||||
|
121
app/src/main/java/com/example/myapplication/composeui/Report.kt
Normal file
121
app/src/main/java/com/example/myapplication/composeui/Report.kt
Normal file
@ -0,0 +1,121 @@
|
||||
package com.example.myapplication.composeui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
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.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.DisplayMode
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.example.myapplication.api.session.ReportRemote
|
||||
import com.example.myapplication.database.entities.composeui.AppViewModelProvider
|
||||
import kotlinx.coroutines.launch
|
||||
import org.threeten.bp.format.DateTimeFormatter
|
||||
import java.util.Date
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun Report(
|
||||
viewModel: ReportViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||
) {
|
||||
val dateStateStart = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
|
||||
val dateStateEnd = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.fillMaxWidth()
|
||||
.padding(all = 10.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
)
|
||||
{
|
||||
Text(
|
||||
text = "Начало периода",
|
||||
style = MaterialTheme.typography.headlineLarge
|
||||
)
|
||||
DatePicker(state = dateStateStart)
|
||||
val selectedDateStart = dateStateStart.selectedDateMillis
|
||||
if (selectedDateStart != null) {
|
||||
viewModel.onUpdate(
|
||||
viewModel.reportUiState.reportDetails.copy(
|
||||
startDate =
|
||||
Date(selectedDateStart)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
viewModel.onUpdate(viewModel.reportUiState.reportDetails.copy(startDate = Date(0)))
|
||||
}
|
||||
Text(
|
||||
text = "Конец периода",
|
||||
style = MaterialTheme.typography.headlineLarge
|
||||
)
|
||||
DatePicker(state = dateStateEnd)
|
||||
val selectedDateEnd = dateStateEnd.selectedDateMillis
|
||||
if (selectedDateEnd != null) {
|
||||
viewModel.onUpdate(
|
||||
viewModel.reportUiState.reportDetails.copy(
|
||||
endDate =
|
||||
Date(selectedDateEnd)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
viewModel.onUpdate(viewModel.reportUiState.reportDetails.copy(endDate = Date(0)))
|
||||
}
|
||||
Button(
|
||||
onClick = { coroutineScope.launch { viewModel.getReport() } },
|
||||
enabled = viewModel.reportUiState.isEntryValid,
|
||||
shape = MaterialTheme.shapes.small,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = "Получить отчет")
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
CardScreen(reportData = viewModel.reportResultUiState.report)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun CardScreen(reportData: List<ReportRemote>) {
|
||||
val dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
|
||||
reportData.forEach {
|
||||
val (cinemaName, ticketDateTime, ticketPrice, ticketQuantity, ticketsPurchased, revenue) = it
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.border(width = 1.dp, color = Color.White, shape = MaterialTheme.shapes.small)
|
||||
) {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(16.dp)
|
||||
.background(color = Color.Transparent)
|
||||
) {
|
||||
Text(text = "Фильм: $cinemaName")
|
||||
Text(text = "Сеанс: ${dateFormatter.format(ticketDateTime)}")
|
||||
Text(text = "Стоимость: $ticketPrice")
|
||||
Text(text = "Максимальное количество билетов: $ticketQuantity")
|
||||
Text(text = "Купили: $ticketsPurchased")
|
||||
Text(text = "Выручка: $revenue")
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package com.example.myapplication.composeui
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.example.myapplication.api.session.ReportRemote
|
||||
import com.example.myapplication.api.session.RestSessionRepository
|
||||
import java.util.Date
|
||||
|
||||
class ReportViewModel(private val serialRepository: RestSessionRepository) : ViewModel() {
|
||||
var reportUiState by mutableStateOf(ReportUiState())
|
||||
private set
|
||||
|
||||
var reportResultUiState by mutableStateOf(ReportResultUiState())
|
||||
private set
|
||||
|
||||
fun onUpdate(reportDetails: ReportDetails) {
|
||||
reportUiState = ReportUiState(
|
||||
reportDetails = reportDetails,
|
||||
isEntryValid = validateInput(reportDetails)
|
||||
)
|
||||
}
|
||||
|
||||
private fun validateInput(uiState: ReportDetails = reportUiState.reportDetails): Boolean {
|
||||
return with(uiState) {
|
||||
startDate != Date(0)
|
||||
&& endDate != Date(0)
|
||||
&& startDate <= endDate
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getReport() {
|
||||
if (validateInput()) {
|
||||
val temp = serialRepository.getReport(
|
||||
reportUiState.reportDetails.startDate,
|
||||
reportUiState.reportDetails.endDate
|
||||
)
|
||||
reportResultUiState = ReportResultUiState(temp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class ReportDetails(
|
||||
val startDate: Date = Date(0),
|
||||
val endDate: Date = Date(0)
|
||||
)
|
||||
|
||||
data class ReportUiState(
|
||||
val reportDetails: ReportDetails = ReportDetails(),
|
||||
val isEntryValid: Boolean = false
|
||||
)
|
||||
|
||||
data class ReportResultUiState(
|
||||
val report: List<ReportRemote> = emptyList()
|
||||
)
|
@ -19,7 +19,6 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Person
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.NavigationBar
|
||||
@ -29,6 +28,7 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
@ -46,7 +46,9 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.example.myapplication.LiveStore
|
||||
import com.example.myapplication.composeui.Cart
|
||||
import com.example.myapplication.composeui.Report
|
||||
import com.example.myapplication.database.entities.composeui.CinemaList
|
||||
import com.example.myapplication.database.entities.composeui.CinemaView
|
||||
import com.example.myapplication.database.entities.composeui.OrderList
|
||||
@ -54,6 +56,7 @@ import com.example.myapplication.database.entities.composeui.OrderView
|
||||
import com.example.myapplication.database.entities.composeui.UserProfile
|
||||
import com.example.myapplication.database.entities.composeui.edit.CinemaEdit
|
||||
import com.example.myapplication.database.entities.composeui.edit.SessionEdit
|
||||
import com.example.myapplication.database.entities.model.UserRole
|
||||
import com.example.myapplication.datastore.DataStoreManager
|
||||
|
||||
@Composable
|
||||
@ -136,28 +139,31 @@ fun Navbar(
|
||||
currentDestination: NavDestination?,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val user = LiveStore.user.observeAsState()
|
||||
NavigationBar(modifier = modifier, containerColor = MaterialTheme.colorScheme.primary) {
|
||||
Screen.bottomBarItems.forEach { screen ->
|
||||
NavigationBarItem(
|
||||
icon = {
|
||||
Icon(
|
||||
screen.icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
},
|
||||
label = { Text(stringResource(screen.resourceId)) },
|
||||
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
|
||||
onClick = {
|
||||
navController.navigate(screen.route) {
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
if (screen.route != Screen.Report.route || user.value?.role == UserRole.ADMIN) {
|
||||
NavigationBarItem(
|
||||
icon = {
|
||||
Icon(
|
||||
screen.icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
},
|
||||
label = { Text(stringResource(screen.resourceId)) },
|
||||
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
|
||||
onClick = {
|
||||
navController.navigate(screen.route) {
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,10 +210,10 @@ fun Navhost(
|
||||
) { backStackEntry ->
|
||||
backStackEntry.arguments?.let { OrderView(it.getInt("id")) }
|
||||
}
|
||||
composable(Screen.Report.route) { Report() }
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MainNavbar(
|
||||
isDarkTheme: MutableState<Boolean>,
|
||||
|
@ -41,13 +41,17 @@ enum class Screen(
|
||||
),
|
||||
UserProfile(
|
||||
"User-profile", R.string.Profile_title, showInBottomBar = false
|
||||
),
|
||||
Report(
|
||||
"Report", R.string.Report_title,
|
||||
);
|
||||
|
||||
companion object {
|
||||
val bottomBarItems = listOf(
|
||||
CinemaList,
|
||||
Cart,
|
||||
OrderList
|
||||
OrderList,
|
||||
Report
|
||||
)
|
||||
|
||||
fun getItem(route: String): Screen? {
|
||||
|
@ -9,6 +9,7 @@ import com.example.myapplication.CinemaApplication
|
||||
import com.example.myapplication.composeui.Authenticator
|
||||
import com.example.myapplication.composeui.AuthenticatorViewModel
|
||||
import com.example.myapplication.composeui.CartViewModel
|
||||
import com.example.myapplication.composeui.ReportViewModel
|
||||
import com.example.myapplication.database.entities.composeui.edit.CinemaEditViewModel
|
||||
import com.example.myapplication.database.entities.composeui.edit.SessionEditViewModel
|
||||
|
||||
@ -68,6 +69,9 @@ object AppViewModelProvider {
|
||||
initializer {
|
||||
AuthenticatorViewModel(cinemaApplication().container.userRestRepository)
|
||||
}
|
||||
initializer {
|
||||
ReportViewModel(cinemaApplication().container.sessionRestRepository)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,26 +1,18 @@
|
||||
package com.example.myapplication.database.entities.composeui
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.PagingData
|
||||
import com.example.myapplication.LiveStore
|
||||
import com.example.myapplication.database.AppDataContainer
|
||||
import com.example.myapplication.database.entities.model.Order
|
||||
import com.example.myapplication.database.entities.repository.OrderRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class OrderListViewModel(
|
||||
private val orderRepository: OrderRepository
|
||||
) : ViewModel() {
|
||||
val orderListUiState: Flow<PagingData<Order>> = orderRepository.getAllOrders(LiveStore.user.value?.uid ?: 0)
|
||||
val orderListUiState: Flow<PagingData<Order>> =
|
||||
orderRepository.getAllOrders(LiveStore.user.value?.uid ?: 0)
|
||||
}
|
||||
|
||||
data class OrderListUiState(val orderList: List<Order> = listOf())
|
@ -46,7 +46,6 @@ fun UserProfile(
|
||||
viewModel: UserProfileViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||
) {
|
||||
var isRegistration by remember { mutableStateOf(false) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val coroutine = rememberCoroutineScope()
|
||||
val errorStringId: Int? = viewModel.userUiState.errorId
|
||||
val errorMessage = if (errorStringId == null) "" else stringResource(errorStringId)
|
||||
@ -54,78 +53,65 @@ fun UserProfile(
|
||||
|
||||
LazyColumn {
|
||||
item {
|
||||
Text(
|
||||
text = "Текущий пользователь: " + (LiveStore.user.value?.login ?: ""),
|
||||
)
|
||||
Button(
|
||||
enabled = user.value != null,
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
dataStoreManager.setLogin("")
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text("Выход")
|
||||
}
|
||||
Text(
|
||||
text = errorMessage,
|
||||
color = Color.Red
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "Логин",
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
BasicTextField(
|
||||
value = viewModel.userUiState.details.login,
|
||||
onValueChange = {
|
||||
viewModel.updateUiState(viewModel.userUiState.details.copy(login = it))
|
||||
},
|
||||
if (user.value != null) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.size(36.dp)
|
||||
.background(MaterialTheme.colorScheme.secondary, RoundedCornerShape(18.dp))
|
||||
.padding(start = 13.dp, top = 8.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Пароль",
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
BasicTextField(
|
||||
value = viewModel.userUiState.details.password,
|
||||
onValueChange = {
|
||||
viewModel.updateUiState(viewModel.userUiState.details.copy(password = it))
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.size(36.dp)
|
||||
.background(MaterialTheme.colorScheme.secondary, RoundedCornerShape(18.dp))
|
||||
.padding(start = 13.dp, top = 8.dp),
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
|
||||
if (isRegistration) {
|
||||
.padding(16.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Подтверждение пароля",
|
||||
text = "Текущий пользователь: " + (user.value?.login ?: ""),
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
Button(
|
||||
enabled = user.value != null,
|
||||
onClick = {
|
||||
coroutine.launch {
|
||||
dataStoreManager.setLogin("")
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) { Text("Выход") }
|
||||
}
|
||||
} else {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = errorMessage,
|
||||
color = Color.Red
|
||||
)
|
||||
Text(
|
||||
text = "Логин",
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
BasicTextField(
|
||||
value = viewModel.userUiState.details.passwordConfirm,
|
||||
value = viewModel.userUiState.details.login,
|
||||
onValueChange = {
|
||||
viewModel.updateUiState(
|
||||
viewModel.userUiState.details.copy(
|
||||
passwordConfirm = it
|
||||
)
|
||||
viewModel.updateUiState(viewModel.userUiState.details.copy(login = it))
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.size(36.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.secondary,
|
||||
RoundedCornerShape(18.dp)
|
||||
)
|
||||
.padding(start = 13.dp, top = 8.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Пароль",
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
BasicTextField(
|
||||
value = viewModel.userUiState.details.password,
|
||||
onValueChange = {
|
||||
viewModel.updateUiState(viewModel.userUiState.details.copy(password = it))
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@ -137,89 +123,110 @@ fun UserProfile(
|
||||
.padding(start = 13.dp, top = 8.dp),
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
}
|
||||
|
||||
if (isRegistration) {
|
||||
Button(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
val flag = viewModel.signUp()
|
||||
isRegistration = !flag
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text("Регистрация")
|
||||
if (isRegistration) {
|
||||
Text(
|
||||
text = "Подтверждение пароля",
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
BasicTextField(
|
||||
value = viewModel.userUiState.details.passwordConfirm,
|
||||
onValueChange = {
|
||||
viewModel.updateUiState(
|
||||
viewModel.userUiState.details.copy(
|
||||
passwordConfirm = it
|
||||
)
|
||||
)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.size(36.dp)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.secondary,
|
||||
RoundedCornerShape(18.dp)
|
||||
)
|
||||
.padding(start = 13.dp, top = 8.dp),
|
||||
visualTransformation = PasswordVisualTransformation()
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = "Уже есть аккаунт? Войти",
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
isRegistration = false
|
||||
}
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
} else {
|
||||
Button(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
if (viewModel.signIn(dataStoreManager)) {
|
||||
navController.navigate(Screen.CinemaList.route)
|
||||
|
||||
if (isRegistration) {
|
||||
Button(
|
||||
onClick = { coroutine.launch { isRegistration = !viewModel.signUp() } },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text("Регистрация")
|
||||
}
|
||||
Text(
|
||||
text = "Уже есть аккаунт? Войти",
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
isRegistration = false
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text("Вход")
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
} else {
|
||||
Button(
|
||||
onClick = {
|
||||
coroutine.launch {
|
||||
if (viewModel.signIn(dataStoreManager)) {
|
||||
navController.navigate(Screen.CinemaList.route)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Text("Вход")
|
||||
}
|
||||
Text(
|
||||
text = "Нет аккаунта? Зарегистрироваться",
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
isRegistration = true
|
||||
}
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = "Нет аккаунта? Зарегистрироваться",
|
||||
modifier = Modifier
|
||||
.clickable {
|
||||
isRegistration = true
|
||||
}
|
||||
.align(Alignment.CenterHorizontally),
|
||||
color = MaterialTheme.colorScheme.onBackground
|
||||
)
|
||||
}
|
||||
val switchColors = SwitchDefaults.colors(
|
||||
checkedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is checked
|
||||
checkedTrackColor = MaterialTheme.colorScheme.secondary, // Change the color of the track when the switch is checked
|
||||
uncheckedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is unchecked
|
||||
uncheckedTrackColor = MaterialTheme.colorScheme.onPrimary // Change the color of the track when the switch is unchecked
|
||||
}
|
||||
val switchColors = SwitchDefaults.colors(
|
||||
checkedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is checked
|
||||
checkedTrackColor = MaterialTheme.colorScheme.secondary, // Change the color of the track when the switch is checked
|
||||
uncheckedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is unchecked
|
||||
uncheckedTrackColor = MaterialTheme.colorScheme.onPrimary // Change the color of the track when the switch is unchecked
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
Text(
|
||||
"Темная тема", modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(5.dp)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
Text(
|
||||
"Темная тема", modifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
.padding(5.dp)
|
||||
)
|
||||
|
||||
Switch(
|
||||
checked = isDarkTheme.value,
|
||||
onCheckedChange = {
|
||||
isDarkTheme.value = !isDarkTheme.value
|
||||
coroutine.launch {
|
||||
if (isDarkTheme.value) {
|
||||
dataStoreManager.setDarkTheme("Dark")
|
||||
} else {
|
||||
dataStoreManager.setDarkTheme("Light")
|
||||
}
|
||||
Switch(
|
||||
checked = isDarkTheme.value,
|
||||
onCheckedChange = {
|
||||
isDarkTheme.value = !isDarkTheme.value
|
||||
coroutine.launch {
|
||||
if (isDarkTheme.value) {
|
||||
dataStoreManager.setDarkTheme("Dark")
|
||||
} else {
|
||||
dataStoreManager.setDarkTheme("Light")
|
||||
}
|
||||
},
|
||||
colors = switchColors
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = switchColors
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
<string name="Order_title">Мои заказы</string>
|
||||
<string name="Profile_title">Профиль</string>
|
||||
<string name="Sessions_title">Сеансы</string>
|
||||
<string name="Report_title">Отчет</string>
|
||||
<string name="Session_dateTime">Время</string>
|
||||
<string name="Save_button">Сохранить</string>
|
||||
<string name="Cinema_empty_description">Записи о фильмах отсутствуют</string>
|
||||
|
210009
server/data.json
210009
server/data.json
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
"name": "fake-db",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "json-server --watch data.json --host 0.0.0.0 -p 8079"
|
||||
"start": "json-server --watch data.json --middlewares ./reportRouter.js --host 0.0.0.0 -p 8079"
|
||||
},
|
||||
"dependencies": {
|
||||
},
|
||||
|
62
server/reportRouter.js
Normal file
62
server/reportRouter.js
Normal file
@ -0,0 +1,62 @@
|
||||
module.exports = (req, res, next) => {
|
||||
const isReportRequest = req.url.startsWith('/report') && req.method === 'GET';
|
||||
|
||||
if (!isReportRequest) {
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { startDate, endDate } = req.query;
|
||||
const { sessions, orders } = require('./data.json');
|
||||
|
||||
const start = new Date(startDate);
|
||||
const end = new Date(endDate);
|
||||
|
||||
// Фильтруем сеансы по периоду
|
||||
const filteredSessions = sessions.filter(session => {
|
||||
const sessionDate = new Date(session.dateTime.replace(/(\d{2}).(\d{2}).(\d{4}) (\d{2}):(\d{2})/, '$3-$2-$1T$4:$5'));
|
||||
// обнуление времени с учетом зоны времени
|
||||
sessionDate.setHours(4, 0, 0, 0);
|
||||
return sessionDate >= start && sessionDate <= end;
|
||||
});
|
||||
|
||||
// Обрабатываем отфильтрованные сеансы для аналитики
|
||||
const reportData = filteredSessions.map(session => {
|
||||
// берем заказ, где сеанс только текущий
|
||||
const relevantOrders = orders
|
||||
.map(orderWithOneSession => ({
|
||||
...orderWithOneSession,
|
||||
sessions: orderWithOneSession.sessions.filter(orderSession => orderSession.id === session.id &&
|
||||
orderSession.cinemaId === session.cinemaId && orderSession.dateTime === session.dateTime)
|
||||
})).filter(order => order.sessions.length > 0);
|
||||
|
||||
const { totalTicketsSold, revenue } = relevantOrders.reduce((accumulator, order) => {
|
||||
const session = order.sessions[0];
|
||||
const tickets = session.count;
|
||||
const sessionRevenue = tickets * session.frozenPrice;
|
||||
|
||||
return {
|
||||
totalTicketsSold: accumulator.totalTicketsSold + tickets,
|
||||
revenue: accumulator.revenue + sessionRevenue
|
||||
};
|
||||
}, { totalTicketsSold: 0, revenue: 0 });
|
||||
|
||||
return {
|
||||
cinema_name: relevantOrders[0].sessions[0].cinema.name,
|
||||
current_ticket_date_time: session.dateTime,
|
||||
current_ticket_price: session.price,
|
||||
max_ticket_quantity: session.maxCount,
|
||||
purchased_tickets: totalTicketsSold,
|
||||
revenue: revenue
|
||||
};
|
||||
});
|
||||
|
||||
const sortedReportData = reportData.sort((a, b) => b.revenue - a.revenue);
|
||||
|
||||
res.json(sortedReportData);
|
||||
} catch (error) {
|
||||
console.error('Error processing report: ', error);
|
||||
res.status(500).json({ message: 'Internal Server Error' });
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user