отчет
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.cinema.CinemaRemote
|
||||||
import com.example.myapplication.api.order.OrderRemote
|
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.SessionFromCinemaRemote
|
||||||
import com.example.myapplication.api.session.SessionRemote
|
import com.example.myapplication.api.session.SessionRemote
|
||||||
import com.example.myapplication.api.session.SessionWithCinemaRemote
|
import com.example.myapplication.api.session.SessionWithCinemaRemote
|
||||||
import com.example.myapplication.api.user.UserRemote
|
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 com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.modules.SerializersModule
|
import kotlinx.serialization.modules.SerializersModule
|
||||||
@ -21,9 +25,10 @@ import retrofit2.http.POST
|
|||||||
import retrofit2.http.PUT
|
import retrofit2.http.PUT
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
interface MyServerService {
|
interface MyServerService {
|
||||||
/*@GET("orders")
|
@GET("orders")
|
||||||
suspend fun getOrders(): List<OrderRemote>
|
suspend fun getOrders(): List<OrderRemote>
|
||||||
|
|
||||||
@GET("users")
|
@GET("users")
|
||||||
@ -40,6 +45,11 @@ interface MyServerService {
|
|||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
): CinemaRemote
|
): CinemaRemote
|
||||||
|
|
||||||
|
@GET("cinemas/{id}?_embed=sessions")
|
||||||
|
suspend fun getCinemaWithSessions(
|
||||||
|
@Path("id") id: Int,
|
||||||
|
): CinemaWithSessionsRemote
|
||||||
|
|
||||||
@POST("cinemas")
|
@POST("cinemas")
|
||||||
suspend fun createCinema(
|
suspend fun createCinema(
|
||||||
@Body cinema: CinemaRemote,
|
@Body cinema: CinemaRemote,
|
||||||
@ -56,11 +66,6 @@ interface MyServerService {
|
|||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
)
|
)
|
||||||
|
|
||||||
@GET("cinemas/{cinemaId}/sessions")
|
|
||||||
suspend fun getSessionsForCinema(
|
|
||||||
@Path("cinemaId") cinemaId: Int
|
|
||||||
): List<SessionFromCinemaRemote>
|
|
||||||
|
|
||||||
@GET("sessions/{id}?_expand=cinema")
|
@GET("sessions/{id}?_expand=cinema")
|
||||||
suspend fun getSession(
|
suspend fun getSession(
|
||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@ -82,21 +87,40 @@ interface MyServerService {
|
|||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
): SessionFromCinemaRemote
|
): SessionFromCinemaRemote
|
||||||
|
|
||||||
@GET("users/{id}")
|
@GET("userssessions?_expand=session")
|
||||||
suspend fun getUserCart(
|
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,
|
@Path("id") id: Int,
|
||||||
): UserRemote
|
): UserSessionRemote
|
||||||
|
|
||||||
@GET("users?_limit=1")
|
@GET("users?_limit=1")
|
||||||
suspend fun getUser(
|
suspend fun getUser(
|
||||||
@Query("login") login: String,
|
@Query("login") login: String,
|
||||||
): List<UserRemote>
|
): 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(
|
suspend fun updateUserCart(
|
||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Body userRemote: UserRemote,
|
@Body userSessionRemote: UserSessionRemote,
|
||||||
): UserRemote
|
): UserSessionRemote
|
||||||
|
|
||||||
@POST("users")
|
@POST("users")
|
||||||
suspend fun createUser(
|
suspend fun createUser(
|
||||||
@ -105,6 +129,7 @@ interface MyServerService {
|
|||||||
|
|
||||||
@GET("orders")
|
@GET("orders")
|
||||||
suspend fun getOrders(
|
suspend fun getOrders(
|
||||||
|
@Query("userId") userId: Int,
|
||||||
@Query("_page") page: Int,
|
@Query("_page") page: Int,
|
||||||
@Query("_limit") limit: Int,
|
@Query("_limit") limit: Int,
|
||||||
): List<OrderRemote>
|
): List<OrderRemote>
|
||||||
@ -123,11 +148,17 @@ interface MyServerService {
|
|||||||
suspend fun updateOrder(
|
suspend fun updateOrder(
|
||||||
@Path("id") id: Int,
|
@Path("id") id: Int,
|
||||||
@Body orderRemote: OrderRemote,
|
@Body orderRemote: OrderRemote,
|
||||||
): OrderRemote*/
|
): OrderRemote
|
||||||
|
|
||||||
|
@GET("report")
|
||||||
|
suspend fun getReport(
|
||||||
|
@Query("startDate") startDate: Date,
|
||||||
|
@Query("endDate") endDate: Date
|
||||||
|
): List<ReportRemote>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
//private const val BASE_URL = "http://192.168.154.166:8080/"
|
//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
|
@Volatile
|
||||||
private var INSTANCE: MyServerService? = null
|
private var INSTANCE: MyServerService? = null
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
package com.example.myapplication.api.cinema
|
package com.example.myapplication.api.cinema
|
||||||
|
|
||||||
|
import android.database.sqlite.SQLiteConstraintException
|
||||||
import androidx.paging.ExperimentalPagingApi
|
import androidx.paging.ExperimentalPagingApi
|
||||||
import androidx.paging.LoadType
|
import androidx.paging.LoadType
|
||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import androidx.paging.RemoteMediator
|
import androidx.paging.RemoteMediator
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import com.example.myapplication.api.MyServerService
|
import com.example.myapplication.api.MyServerService
|
||||||
import com.example.myapplication.api.session.toSession
|
|
||||||
import com.example.myapplication.database.AppDatabase
|
import com.example.myapplication.database.AppDatabase
|
||||||
import com.example.myapplication.database.entities.model.Cinema
|
import com.example.myapplication.database.entities.model.Cinema
|
||||||
import com.example.myapplication.database.entities.repository.OfflineCinemaRepository
|
import com.example.myapplication.database.entities.repository.OfflineCinemaRepository
|
||||||
@ -55,10 +55,8 @@ class CinemaRemoteMediator(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
val cinemas = service.getCinemas(page, state.config.pageSize).map { it.toCinema() }
|
val cinemas = service.getCinemas(page, state.config.pageSize).map { it.toCinema() }
|
||||||
val cinemasWithSessions = cinemas.map { cinema ->
|
val sessionsFromCinemas = cinemas.map { cinema ->
|
||||||
service.getSessionsForCinema(cinema.uid).map {
|
service.getCinemaWithSessions(cinema.uid).toSessions()
|
||||||
service.getSession(it.id).toSession()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
val endOfPaginationReached = cinemas.isEmpty()
|
val endOfPaginationReached = cinemas.isEmpty()
|
||||||
database.withTransaction {
|
database.withTransaction {
|
||||||
@ -79,10 +77,11 @@ class CinemaRemoteMediator(
|
|||||||
}
|
}
|
||||||
dbRemoteKeyRepository.createRemoteKeys(keys)
|
dbRemoteKeyRepository.createRemoteKeys(keys)
|
||||||
dbCinemaRepository.insertCinemas(cinemas)
|
dbCinemaRepository.insertCinemas(cinemas)
|
||||||
cinemasWithSessions.forEach {
|
sessionsFromCinemas.forEach {
|
||||||
try {
|
try {
|
||||||
dbSessionRepository.insertSessions(it)
|
dbSessionRepository.insertSessions(it)
|
||||||
} catch (_: Exception) {
|
} catch(_:Exception) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,6 +90,8 @@ class CinemaRemoteMediator(
|
|||||||
return MediatorResult.Error(exception)
|
return MediatorResult.Error(exception)
|
||||||
} catch (exception: HttpException) {
|
} catch (exception: HttpException) {
|
||||||
return MediatorResult.Error(exception)
|
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 {
|
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(
|
SessionFromCinema(
|
||||||
x.id,
|
sessionFromCinemaRemote.id,
|
||||||
x.dateTime,
|
sessionFromCinemaRemote.dateTime,
|
||||||
x.price,
|
sessionFromCinemaRemote.price,
|
||||||
x.maxCount - service.getOrders().flatMap { order ->
|
sessionFromCinemaRemote.maxCount - service.getOrders().flatMap
|
||||||
order.sessions.filter { session -> session.id == x.id }
|
{ order ->
|
||||||
|
order.sessions.filter { session ->
|
||||||
|
session.id == sessionFromCinemaRemote.id &&
|
||||||
|
session.cinemaId == sessionFromCinemaRemote.cinemaId &&
|
||||||
|
session.cinema.name == cinemaWithSessions.name
|
||||||
|
}
|
||||||
}.sumOf { session -> session.count },
|
}.sumOf { session -> session.count },
|
||||||
uid
|
uid
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return CinemaWithSessions(cinema, sessions)
|
return CinemaWithSessions(cinemaWithSessions.toCinema(), sessions)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun insertCinema(cinema: Cinema) {
|
override suspend fun insertCinema(cinema: Cinema) {
|
||||||
@ -72,15 +77,11 @@ class RestCinemaRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteCinema(cinema: Cinema) {
|
override suspend fun deleteCinema(cinema: Cinema) {
|
||||||
val cart = service.getUsers()
|
val cart = service.getUsersSessions()
|
||||||
cart.forEach { userRemote ->
|
cart.forEach { userSessionRemote ->
|
||||||
userRemote.sessions = userRemote.sessions.filter { x -> x.cinemaId != cinema.uid }
|
if (userSessionRemote.session.cinemaId == cinema.uid) {
|
||||||
service.updateUserCart(userRemote.id, userRemote)
|
service.deleteUserSession(userSessionRemote.id)
|
||||||
}
|
}
|
||||||
val orders = service.getOrders()
|
|
||||||
orders.forEach { orderRemote ->
|
|
||||||
orderRemote.sessions = orderRemote.sessions.filter { x -> x.cinemaId != cinema.uid }
|
|
||||||
service.updateOrder(orderRemote.id, orderRemote)
|
|
||||||
}
|
}
|
||||||
service.deleteCinema(cinema.uid)
|
service.deleteCinema(cinema.uid)
|
||||||
dbCinemaRepository.deleteCinema(cinema)
|
dbCinemaRepository.deleteCinema(cinema)
|
||||||
|
@ -5,6 +5,7 @@ import androidx.paging.LoadType
|
|||||||
import androidx.paging.PagingState
|
import androidx.paging.PagingState
|
||||||
import androidx.paging.RemoteMediator
|
import androidx.paging.RemoteMediator
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
|
import com.example.myapplication.LiveStore
|
||||||
import com.example.myapplication.api.MyServerService
|
import com.example.myapplication.api.MyServerService
|
||||||
import com.example.myapplication.database.AppDatabase
|
import com.example.myapplication.database.AppDatabase
|
||||||
import com.example.myapplication.database.entities.model.Order
|
import com.example.myapplication.database.entities.model.Order
|
||||||
@ -51,7 +52,8 @@ class OrderRemoteMediator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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()
|
val endOfPaginationReached = orders.isEmpty()
|
||||||
database.withTransaction {
|
database.withTransaction {
|
||||||
if (loadType == LoadType.REFRESH) {
|
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 {
|
override suspend fun insertOrder(order: Order): Long {
|
||||||
|
@ -13,14 +13,15 @@ class RestOrderSessionRepository(
|
|||||||
) : OrderSessionRepository {
|
) : OrderSessionRepository {
|
||||||
override suspend fun insertOrderSession(orderSessionCrossRef: OrderSessionCrossRef) {
|
override suspend fun insertOrderSession(orderSessionCrossRef: OrderSessionCrossRef) {
|
||||||
var orderRemote = service.getOrder(orderSessionCrossRef.orderId)
|
var orderRemote = service.getOrder(orderSessionCrossRef.orderId)
|
||||||
val session = service.getSession(orderSessionCrossRef.sessionId).toSession()
|
val session = service.getSession(orderSessionCrossRef.sessionId)
|
||||||
|
|
||||||
val sessionFromOrder = SessionFromOrderRemote(
|
val sessionFromOrder = SessionFromOrderRemote(
|
||||||
session.uid,
|
session.id,
|
||||||
session.dateTime,
|
session.dateTime,
|
||||||
session.price,
|
session.price,
|
||||||
orderSessionCrossRef.count,
|
orderSessionCrossRef.count,
|
||||||
session.cinemaId
|
session.cinemaId,
|
||||||
|
session.cinema
|
||||||
)
|
)
|
||||||
|
|
||||||
val updatedSessions = orderRemote.sessions.toMutableList()
|
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.OfflineSessionRepository
|
||||||
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
||||||
import com.example.myapplication.database.entities.repository.SessionRepository
|
import com.example.myapplication.database.entities.repository.SessionRepository
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
class RestSessionRepository(
|
class RestSessionRepository(
|
||||||
private val service: MyServerService,
|
private val service: MyServerService,
|
||||||
@ -33,10 +34,11 @@ class RestSessionRepository(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteSession(session: Session) {
|
override suspend fun deleteSession(session: Session) {
|
||||||
val cart = service.getUsers()
|
val cart = service.getUsersSessions()
|
||||||
cart.forEach { userRemote ->
|
cart.forEach { userSessionRemote ->
|
||||||
userRemote.sessions = userRemote.sessions.filter { x -> x.id != session.uid }
|
if (userSessionRemote.session.id == session.uid) {
|
||||||
service.updateUserCart(userRemote.id, userRemote)
|
service.deleteUserSession(userSessionRemote.id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
val orders = service.getOrders()
|
val orders = service.getOrders()
|
||||||
orders.forEach { orderRemote ->
|
orders.forEach { orderRemote ->
|
||||||
@ -48,4 +50,8 @@ class RestSessionRepository(
|
|||||||
dbOrderSessionRepository.deleteSessionsByUid(session.uid)
|
dbOrderSessionRepository.deleteSessionsByUid(session.uid)
|
||||||
dbSessionRepository.deleteSession(session)
|
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
|
package com.example.myapplication.api.session
|
||||||
|
|
||||||
import com.example.myapplication.database.entities.model.Session
|
|
||||||
import com.example.myapplication.database.entities.model.SessionFromCinema
|
import com.example.myapplication.database.entities.model.SessionFromCinema
|
||||||
import kotlinx.serialization.Contextual
|
import kotlinx.serialization.Contextual
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
@ -24,11 +23,3 @@ fun SessionFromCinemaRemote.toSessionFromCinema(): SessionFromCinema = SessionFr
|
|||||||
availableCount,
|
availableCount,
|
||||||
cinemaId
|
cinemaId
|
||||||
)
|
)
|
||||||
|
|
||||||
fun SessionFromCinema.toSessionFromCinemaRemote(): SessionFromCinemaRemote = SessionFromCinemaRemote(
|
|
||||||
uid,
|
|
||||||
dateTime,
|
|
||||||
price,
|
|
||||||
availableCount,
|
|
||||||
cinemaId
|
|
||||||
)
|
|
@ -14,9 +14,10 @@ class SessionFromOrderRemote(
|
|||||||
val frozenPrice: Double = 0.0,
|
val frozenPrice: Double = 0.0,
|
||||||
val count: Int = 0,
|
val count: Int = 0,
|
||||||
val cinemaId: Int = 0,
|
val cinemaId: Int = 0,
|
||||||
|
val cinema: CinemaRemote,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun SessionFromOrderRemote.toSessionFromOrder(cinema: CinemaRemote): SessionFromOrder =
|
fun SessionFromOrderRemote.toSessionFromOrder(): SessionFromOrder =
|
||||||
SessionFromOrder(
|
SessionFromOrder(
|
||||||
id, dateTime, frozenPrice, count, cinemaId, cinema.toCinema()
|
id, dateTime, frozenPrice, count, cinemaId, cinema.toCinema()
|
||||||
)
|
)
|
@ -30,25 +30,21 @@ class RestUserRepository(
|
|||||||
override suspend fun getCartByUser(userId: Int): List<SessionFromCart> {
|
override suspend fun getCartByUser(userId: Int): List<SessionFromCart> {
|
||||||
val cart = service.getUserCart(userId)
|
val cart = service.getUserCart(userId)
|
||||||
dbUserSessionRepository.deleteUserSessions(userId)
|
dbUserSessionRepository.deleteUserSessions(userId)
|
||||||
cart.sessions.map { sessionFromCartRemote ->
|
cart.map { sessionFromCartRemote ->
|
||||||
dbUserSessionRepository.insertUserSession(
|
dbUserSessionRepository.insertUserSession(
|
||||||
UserSessionCrossRef(
|
UserSessionCrossRef(
|
||||||
userId,
|
userId,
|
||||||
sessionFromCartRemote.id,
|
sessionFromCartRemote.sessionId,
|
||||||
sessionFromCartRemote.count
|
sessionFromCartRemote.count
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
return cart.map {
|
||||||
return cart.sessions.map {
|
val cinema = service.getCinema(it.session.cinemaId)
|
||||||
val session = service.getSession(it.id)
|
|
||||||
it.toSessionFromCart(
|
it.toSessionFromCart(
|
||||||
session.cinema,
|
it.session.maxCount - service.getOrders().flatMap { order ->
|
||||||
session.dateTime,
|
|
||||||
session.price,
|
|
||||||
session.maxCount - service.getOrders().flatMap { order ->
|
|
||||||
order.sessions.filter { session -> session.id == it.id }
|
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 login: String = "",
|
||||||
val password: String = "",
|
val password: String = "",
|
||||||
val role: Int = -1,
|
val role: Int = -1,
|
||||||
var sessions: List<SessionFromCartRemote> = emptyList()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun User.toUserRemote(): UserRemote = UserRemote(
|
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.MyServerService
|
||||||
import com.example.myapplication.api.session.SessionFromCartRemote
|
import com.example.myapplication.api.session.SessionFromCartRemote
|
||||||
import com.example.myapplication.api.session.toSession
|
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.model.UserSessionCrossRef
|
||||||
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
import com.example.myapplication.database.entities.repository.OfflineUserSessionRepository
|
||||||
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
||||||
@ -12,51 +13,44 @@ class RestUserSessionRepository(
|
|||||||
private val dbUserSessionRepository: OfflineUserSessionRepository
|
private val dbUserSessionRepository: OfflineUserSessionRepository
|
||||||
) : UserSessionRepository {
|
) : UserSessionRepository {
|
||||||
override suspend fun insertUserSession(userSessionCrossRef: UserSessionCrossRef) {
|
override suspend fun insertUserSession(userSessionCrossRef: UserSessionCrossRef) {
|
||||||
var cartSessions = service.getUserCart(userSessionCrossRef.userId)
|
val cartSessions = service.getUserCart(userSessionCrossRef.userId)
|
||||||
cartSessions.sessions.forEach { session ->
|
cartSessions.forEach { session ->
|
||||||
if (session.id == userSessionCrossRef.sessionId)
|
if (session.sessionId == userSessionCrossRef.sessionId)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val session = service.getSession(userSessionCrossRef.sessionId).toSession()
|
service.createUserSession(UserSessionRemote(id = 0,
|
||||||
|
userId = userSessionCrossRef.userId,
|
||||||
val sessionFromCart = SessionFromCartRemote(
|
sessionId = userSessionCrossRef.sessionId,
|
||||||
session.uid,
|
count = userSessionCrossRef.count
|
||||||
userSessionCrossRef.count,
|
))
|
||||||
session.cinemaId,
|
|
||||||
)
|
|
||||||
|
|
||||||
val updatedSessions = cartSessions.sessions.toMutableList()
|
|
||||||
updatedSessions.add(sessionFromCart)
|
|
||||||
|
|
||||||
cartSessions = cartSessions.copy(sessions = updatedSessions)
|
|
||||||
service.updateUserCart(userSessionCrossRef.userId, cartSessions)
|
|
||||||
dbUserSessionRepository.insertUserSession(userSessionCrossRef)
|
dbUserSessionRepository.insertUserSession(userSessionCrossRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun updateUserSession(userSessionCrossRef: 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) {
|
if (userSessionCrossRef.count <= 0) {
|
||||||
userRemote.sessions =
|
service.deleteUserSession(userSessionRemote.id)
|
||||||
userRemote.sessions.filter { x -> x.id != userSessionCrossRef.sessionId }
|
dbUserSessionRepository.deleteUserSession(userSessionCrossRef)
|
||||||
} else
|
return
|
||||||
userRemote.sessions.forEach {
|
}
|
||||||
if (it.id == userSessionCrossRef.sessionId) {
|
userSessionRemote.count = userSessionCrossRef.count
|
||||||
it.count = userSessionCrossRef.count
|
service.updateUserCart(userSessionRemote.id, userSessionRemote)
|
||||||
}
|
|
||||||
}
|
|
||||||
service.updateUserCart(userSessionCrossRef.userId, userRemote)
|
|
||||||
dbUserSessionRepository.updateUserSession(userSessionCrossRef)
|
dbUserSessionRepository.updateUserSession(userSessionCrossRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteUserSession(userSessionCrossRef: 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)
|
dbUserSessionRepository.deleteUserSession(userSessionCrossRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun deleteUserSessions(userId: Int) {
|
override suspend fun deleteUserSessions(userId: Int) {
|
||||||
val userRemote = service.getUserCart(userId)
|
val cart = service.getUserCart(userId)
|
||||||
userRemote.sessions = emptyList()
|
cart.forEach {
|
||||||
service.updateUserCart(userId, userRemote)
|
service.deleteUserSession(it.id)
|
||||||
|
}
|
||||||
dbUserSessionRepository.deleteUserSessions(userId)
|
dbUserSessionRepository.deleteUserSessions(userId)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,7 +15,6 @@ fun Authenticator(
|
|||||||
dataStoreManager: DataStoreManager,
|
dataStoreManager: DataStoreManager,
|
||||||
viewModel: AuthenticatorViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
viewModel: AuthenticatorViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val login = dataStoreManager.getLogin().collectAsState(initial = "").value
|
val login = dataStoreManager.getLogin().collectAsState(initial = "").value
|
||||||
|
@ -164,7 +164,7 @@ private fun SessionListItem(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onChangeCount: (SessionFromCart, Int) -> Unit,
|
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 dateFormatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
|
||||||
val formattedDate = dateFormatter.format(session.dateTime)
|
val formattedDate = dateFormatter.format(session.dateTime)
|
||||||
@ -203,7 +203,7 @@ private fun SessionListItem(
|
|||||||
Text(
|
Text(
|
||||||
text = "${session.cinema.name}, ${session.cinema.year}\n" +
|
text = "${session.cinema.name}, ${session.cinema.year}\n" +
|
||||||
"Цена: ${session.price}\n" +
|
"Цена: ${session.price}\n" +
|
||||||
"${currentCount}/${session.availableCount}",
|
"${session.count}/${session.availableCount}",
|
||||||
color = MaterialTheme.colorScheme.onSecondary
|
color = MaterialTheme.colorScheme.onSecondary
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -219,7 +219,7 @@ private fun SessionListItem(
|
|||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { onChangeCount(session, --currentCount) }
|
onClick = { onChangeCount(session, session.count - 1) }
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = ImageVector.vectorResource(id = R.drawable.minus),
|
imageVector = ImageVector.vectorResource(id = R.drawable.minus),
|
||||||
@ -230,7 +230,7 @@ private fun SessionListItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "$currentCount",
|
text = "${session.count}",
|
||||||
color = MaterialTheme.colorScheme.onBackground
|
color = MaterialTheme.colorScheme.onBackground
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ private fun SessionListItem(
|
|||||||
onClick = {
|
onClick = {
|
||||||
onChangeCount(
|
onChangeCount(
|
||||||
session,
|
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.UserRepository
|
||||||
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
import com.example.myapplication.database.entities.repository.UserSessionRepository
|
||||||
import org.threeten.bp.LocalDateTime
|
import org.threeten.bp.LocalDateTime
|
||||||
|
import java.util.LinkedList
|
||||||
|
|
||||||
class CartViewModel(
|
class CartViewModel(
|
||||||
private val userSessionRepository: UserSessionRepository,
|
private val userSessionRepository: UserSessionRepository,
|
||||||
@ -22,6 +23,19 @@ class CartViewModel(
|
|||||||
private val orderSessionRepository: OrderSessionRepository,
|
private val orderSessionRepository: OrderSessionRepository,
|
||||||
private val userRepository: UserRepository,
|
private val userRepository: UserRepository,
|
||||||
) : ViewModel() {
|
) : 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 isLoading: Boolean = false
|
||||||
var cartUiState by mutableStateOf(CartUiState())
|
var cartUiState by mutableStateOf(CartUiState())
|
||||||
private set
|
private set
|
||||||
@ -71,6 +85,7 @@ class CartViewModel(
|
|||||||
isLoading = true
|
isLoading = true
|
||||||
val userId: Int = LiveStore.user.value?.uid ?: return false
|
val userId: Int = LiveStore.user.value?.uid ?: return false
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
|
isLoading = false
|
||||||
removeFromCart(session, count)
|
removeFromCart(session, count)
|
||||||
return false
|
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.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Person
|
import androidx.compose.material.icons.filled.Person
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.NavigationBar
|
||||||
@ -29,6 +28,7 @@ import androidx.compose.material3.Text
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableState
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
@ -46,7 +46,9 @@ import androidx.navigation.compose.composable
|
|||||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
|
import com.example.myapplication.LiveStore
|
||||||
import com.example.myapplication.composeui.Cart
|
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.CinemaList
|
||||||
import com.example.myapplication.database.entities.composeui.CinemaView
|
import com.example.myapplication.database.entities.composeui.CinemaView
|
||||||
import com.example.myapplication.database.entities.composeui.OrderList
|
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.UserProfile
|
||||||
import com.example.myapplication.database.entities.composeui.edit.CinemaEdit
|
import com.example.myapplication.database.entities.composeui.edit.CinemaEdit
|
||||||
import com.example.myapplication.database.entities.composeui.edit.SessionEdit
|
import com.example.myapplication.database.entities.composeui.edit.SessionEdit
|
||||||
|
import com.example.myapplication.database.entities.model.UserRole
|
||||||
import com.example.myapplication.datastore.DataStoreManager
|
import com.example.myapplication.datastore.DataStoreManager
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@ -136,28 +139,31 @@ fun Navbar(
|
|||||||
currentDestination: NavDestination?,
|
currentDestination: NavDestination?,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
val user = LiveStore.user.observeAsState()
|
||||||
NavigationBar(modifier = modifier, containerColor = MaterialTheme.colorScheme.primary) {
|
NavigationBar(modifier = modifier, containerColor = MaterialTheme.colorScheme.primary) {
|
||||||
Screen.bottomBarItems.forEach { screen ->
|
Screen.bottomBarItems.forEach { screen ->
|
||||||
NavigationBarItem(
|
if (screen.route != Screen.Report.route || user.value?.role == UserRole.ADMIN) {
|
||||||
icon = {
|
NavigationBarItem(
|
||||||
Icon(
|
icon = {
|
||||||
screen.icon,
|
Icon(
|
||||||
contentDescription = null,
|
screen.icon,
|
||||||
tint = MaterialTheme.colorScheme.secondary
|
contentDescription = null,
|
||||||
)
|
tint = MaterialTheme.colorScheme.secondary
|
||||||
},
|
)
|
||||||
label = { Text(stringResource(screen.resourceId)) },
|
},
|
||||||
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
|
label = { Text(stringResource(screen.resourceId)) },
|
||||||
onClick = {
|
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
|
||||||
navController.navigate(screen.route) {
|
onClick = {
|
||||||
popUpTo(navController.graph.findStartDestination().id) {
|
navController.navigate(screen.route) {
|
||||||
saveState = true
|
popUpTo(navController.graph.findStartDestination().id) {
|
||||||
|
saveState = true
|
||||||
|
}
|
||||||
|
launchSingleTop = true
|
||||||
|
restoreState = true
|
||||||
}
|
}
|
||||||
launchSingleTop = true
|
|
||||||
restoreState = true
|
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,10 +210,10 @@ fun Navhost(
|
|||||||
) { backStackEntry ->
|
) { backStackEntry ->
|
||||||
backStackEntry.arguments?.let { OrderView(it.getInt("id")) }
|
backStackEntry.arguments?.let { OrderView(it.getInt("id")) }
|
||||||
}
|
}
|
||||||
|
composable(Screen.Report.route) { Report() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainNavbar(
|
fun MainNavbar(
|
||||||
isDarkTheme: MutableState<Boolean>,
|
isDarkTheme: MutableState<Boolean>,
|
||||||
|
@ -41,13 +41,17 @@ enum class Screen(
|
|||||||
),
|
),
|
||||||
UserProfile(
|
UserProfile(
|
||||||
"User-profile", R.string.Profile_title, showInBottomBar = false
|
"User-profile", R.string.Profile_title, showInBottomBar = false
|
||||||
|
),
|
||||||
|
Report(
|
||||||
|
"Report", R.string.Report_title,
|
||||||
);
|
);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val bottomBarItems = listOf(
|
val bottomBarItems = listOf(
|
||||||
CinemaList,
|
CinemaList,
|
||||||
Cart,
|
Cart,
|
||||||
OrderList
|
OrderList,
|
||||||
|
Report
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getItem(route: String): Screen? {
|
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.Authenticator
|
||||||
import com.example.myapplication.composeui.AuthenticatorViewModel
|
import com.example.myapplication.composeui.AuthenticatorViewModel
|
||||||
import com.example.myapplication.composeui.CartViewModel
|
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.CinemaEditViewModel
|
||||||
import com.example.myapplication.database.entities.composeui.edit.SessionEditViewModel
|
import com.example.myapplication.database.entities.composeui.edit.SessionEditViewModel
|
||||||
|
|
||||||
@ -68,6 +69,9 @@ object AppViewModelProvider {
|
|||||||
initializer {
|
initializer {
|
||||||
AuthenticatorViewModel(cinemaApplication().container.userRestRepository)
|
AuthenticatorViewModel(cinemaApplication().container.userRestRepository)
|
||||||
}
|
}
|
||||||
|
initializer {
|
||||||
|
ReportViewModel(cinemaApplication().container.sessionRestRepository)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,18 @@
|
|||||||
package com.example.myapplication.database.entities.composeui
|
package com.example.myapplication.database.entities.composeui
|
||||||
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import com.example.myapplication.LiveStore
|
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.model.Order
|
||||||
import com.example.myapplication.database.entities.repository.OrderRepository
|
import com.example.myapplication.database.entities.repository.OrderRepository
|
||||||
import kotlinx.coroutines.flow.Flow
|
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(
|
class OrderListViewModel(
|
||||||
private val orderRepository: OrderRepository
|
private val orderRepository: OrderRepository
|
||||||
) : ViewModel() {
|
) : 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())
|
data class OrderListUiState(val orderList: List<Order> = listOf())
|
@ -46,7 +46,6 @@ fun UserProfile(
|
|||||||
viewModel: UserProfileViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
viewModel: UserProfileViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||||
) {
|
) {
|
||||||
var isRegistration by remember { mutableStateOf(false) }
|
var isRegistration by remember { mutableStateOf(false) }
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
val coroutine = rememberCoroutineScope()
|
val coroutine = rememberCoroutineScope()
|
||||||
val errorStringId: Int? = viewModel.userUiState.errorId
|
val errorStringId: Int? = viewModel.userUiState.errorId
|
||||||
val errorMessage = if (errorStringId == null) "" else stringResource(errorStringId)
|
val errorMessage = if (errorStringId == null) "" else stringResource(errorStringId)
|
||||||
@ -54,78 +53,65 @@ fun UserProfile(
|
|||||||
|
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
item {
|
item {
|
||||||
Text(
|
if (user.value != null) {
|
||||||
text = "Текущий пользователь: " + (LiveStore.user.value?.login ?: ""),
|
Column(
|
||||||
)
|
|
||||||
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))
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.padding(16.dp),
|
||||||
.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) {
|
|
||||||
Text(
|
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)
|
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||||
)
|
)
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
value = viewModel.userUiState.details.passwordConfirm,
|
value = viewModel.userUiState.details.login,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
viewModel.updateUiState(
|
viewModel.updateUiState(viewModel.userUiState.details.copy(login = it))
|
||||||
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)
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = "Пароль",
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||||
|
)
|
||||||
|
BasicTextField(
|
||||||
|
value = viewModel.userUiState.details.password,
|
||||||
|
onValueChange = {
|
||||||
|
viewModel.updateUiState(viewModel.userUiState.details.copy(password = it))
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -137,89 +123,110 @@ fun UserProfile(
|
|||||||
.padding(start = 13.dp, top = 8.dp),
|
.padding(start = 13.dp, top = 8.dp),
|
||||||
visualTransformation = PasswordVisualTransformation()
|
visualTransformation = PasswordVisualTransformation()
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
if (isRegistration) {
|
if (isRegistration) {
|
||||||
Button(
|
Text(
|
||||||
onClick = {
|
text = "Подтверждение пароля",
|
||||||
coroutineScope.launch {
|
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||||
val flag = viewModel.signUp()
|
)
|
||||||
isRegistration = !flag
|
BasicTextField(
|
||||||
}
|
value = viewModel.userUiState.details.passwordConfirm,
|
||||||
},
|
onValueChange = {
|
||||||
modifier = Modifier
|
viewModel.updateUiState(
|
||||||
.fillMaxWidth()
|
viewModel.userUiState.details.copy(
|
||||||
.padding(8.dp)
|
passwordConfirm = it
|
||||||
) {
|
)
|
||||||
Text("Регистрация")
|
)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.size(36.dp)
|
||||||
|
.background(
|
||||||
|
MaterialTheme.colorScheme.secondary,
|
||||||
|
RoundedCornerShape(18.dp)
|
||||||
|
)
|
||||||
|
.padding(start = 13.dp, top = 8.dp),
|
||||||
|
visualTransformation = PasswordVisualTransformation()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Text(
|
|
||||||
text = "Уже есть аккаунт? Войти",
|
if (isRegistration) {
|
||||||
modifier = Modifier
|
Button(
|
||||||
.clickable {
|
onClick = { coroutine.launch { isRegistration = !viewModel.signUp() } },
|
||||||
isRegistration = false
|
modifier = Modifier
|
||||||
}
|
.fillMaxWidth()
|
||||||
.align(Alignment.CenterHorizontally),
|
.padding(8.dp)
|
||||||
color = MaterialTheme.colorScheme.onBackground
|
) {
|
||||||
)
|
Text("Регистрация")
|
||||||
} else {
|
}
|
||||||
Button(
|
Text(
|
||||||
onClick = {
|
text = "Уже есть аккаунт? Войти",
|
||||||
coroutineScope.launch {
|
modifier = Modifier
|
||||||
if (viewModel.signIn(dataStoreManager)) {
|
.clickable {
|
||||||
navController.navigate(Screen.CinemaList.route)
|
isRegistration = false
|
||||||
}
|
}
|
||||||
}
|
.align(Alignment.CenterHorizontally),
|
||||||
},
|
color = MaterialTheme.colorScheme.onBackground
|
||||||
modifier = Modifier
|
)
|
||||||
.fillMaxWidth()
|
} else {
|
||||||
.padding(8.dp)
|
Button(
|
||||||
) {
|
onClick = {
|
||||||
Text("Вход")
|
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
|
val switchColors = SwitchDefaults.colors(
|
||||||
checkedTrackColor = MaterialTheme.colorScheme.secondary, // Change the color of the track when the switch is checked
|
checkedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is checked
|
||||||
uncheckedThumbColor = MaterialTheme.colorScheme.primary, // Change the color when the switch is unchecked
|
checkedTrackColor = MaterialTheme.colorScheme.secondary, // Change the color of the track when the switch is checked
|
||||||
uncheckedTrackColor = MaterialTheme.colorScheme.onPrimary // Change the color of the track when the switch is unchecked
|
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(
|
Switch(
|
||||||
checked = isDarkTheme.value,
|
checked = isDarkTheme.value,
|
||||||
onCheckedChange = {
|
onCheckedChange = {
|
||||||
isDarkTheme.value = !isDarkTheme.value
|
isDarkTheme.value = !isDarkTheme.value
|
||||||
coroutine.launch {
|
coroutine.launch {
|
||||||
if (isDarkTheme.value) {
|
if (isDarkTheme.value) {
|
||||||
dataStoreManager.setDarkTheme("Dark")
|
dataStoreManager.setDarkTheme("Dark")
|
||||||
} else {
|
} else {
|
||||||
dataStoreManager.setDarkTheme("Light")
|
dataStoreManager.setDarkTheme("Light")
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
colors = switchColors
|
},
|
||||||
)
|
colors = switchColors
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
<string name="Order_title">Мои заказы</string>
|
<string name="Order_title">Мои заказы</string>
|
||||||
<string name="Profile_title">Профиль</string>
|
<string name="Profile_title">Профиль</string>
|
||||||
<string name="Sessions_title">Сеансы</string>
|
<string name="Sessions_title">Сеансы</string>
|
||||||
|
<string name="Report_title">Отчет</string>
|
||||||
<string name="Session_dateTime">Время</string>
|
<string name="Session_dateTime">Время</string>
|
||||||
<string name="Save_button">Сохранить</string>
|
<string name="Save_button">Сохранить</string>
|
||||||
<string name="Cinema_empty_description">Записи о фильмах отсутствуют</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",
|
"name": "fake-db",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"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": {
|
"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