lab5
This commit is contained in:
parent
71f82033ec
commit
e4a08474a5
@ -1,4 +1,3 @@
|
|||||||
package ru.ulstu.is.pmu.api
|
package ru.ulstu.`is`.pmu.api
|
||||||
|
|
||||||
class ApiStatus {
|
enum class ApiStatus { LOADING, ERROR, DONE }
|
||||||
}
|
|
@ -21,9 +21,16 @@ import retrofit2.http.Path
|
|||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
import ru.ulstu.`is`.pmu.api.model.UserRemote
|
import ru.ulstu.`is`.pmu.api.model.UserRemote
|
||||||
import ru.ulstu.`is`.pmu.api.model.TaskRemote
|
import ru.ulstu.`is`.pmu.api.model.TaskRemote
|
||||||
|
import ru.ulstu.`is`.pmu.api.report.ReportRemote
|
||||||
|
|
||||||
|
|
||||||
interface MyServerService {
|
interface MyServerService {
|
||||||
|
@GET("report")
|
||||||
|
suspend fun getReportInfo(
|
||||||
|
@Query("fromDate") fromDate: String,
|
||||||
|
@Query("toDate") toDate: String
|
||||||
|
): List<ReportRemote>
|
||||||
|
|
||||||
@GET("users")
|
@GET("users")
|
||||||
suspend fun getUsers(): List<UserRemote>
|
suspend fun getUsers(): List<UserRemote>
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ data class TaskRemote(
|
|||||||
val name: String = "",
|
val name: String = "",
|
||||||
val description: String = "",
|
val description: String = "",
|
||||||
val endDate: String = "",
|
val endDate: String = "",
|
||||||
val favorite: Boolean,
|
val favorite: Boolean = false, // Значение по умолчанию
|
||||||
val userId: Int,
|
val userId: Int = 1, // Значение по умолчанию
|
||||||
)
|
)
|
||||||
|
|
||||||
fun TaskRemote.toTask(): Task = Task(
|
fun TaskRemote.toTask(): Task = Task(
|
||||||
@ -18,7 +18,7 @@ fun TaskRemote.toTask(): Task = Task(
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
endDate,
|
endDate,
|
||||||
favorite = false,
|
favorite,
|
||||||
userId
|
userId
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,6 +27,6 @@ fun Task.toTaskRemote(): TaskRemote = TaskRemote(
|
|||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
endDate,
|
endDate,
|
||||||
favorite = false,
|
favorite,
|
||||||
userId = 1
|
userId = 1
|
||||||
)
|
)
|
@ -1,4 +1,11 @@
|
|||||||
package ru.ulstu.is.pmu.api.report
|
package ru.ulstu.`is`.pmu.api.report
|
||||||
|
|
||||||
class ReportRemote {
|
import kotlinx.serialization.Serializable
|
||||||
}
|
|
||||||
|
@Serializable
|
||||||
|
data class ReportRemote(
|
||||||
|
val id: Int = 0,
|
||||||
|
val name: String = "",
|
||||||
|
val description: String = "",
|
||||||
|
val endDate: String = ""
|
||||||
|
)
|
@ -10,12 +10,16 @@ import ru.ulstu.`is`.pmu.api.MyServerService
|
|||||||
import ru.ulstu.`is`.pmu.api.user.RestUserRepository
|
import ru.ulstu.`is`.pmu.api.user.RestUserRepository
|
||||||
import ru.ulstu.`is`.pmu.api.model.toTask
|
import ru.ulstu.`is`.pmu.api.model.toTask
|
||||||
import ru.ulstu.`is`.pmu.api.model.toTaskRemote
|
import ru.ulstu.`is`.pmu.api.model.toTaskRemote
|
||||||
|
import ru.ulstu.`is`.pmu.api.report.ReportRemote
|
||||||
import ru.ulstu.`is`.pmu.common.AppContainer
|
import ru.ulstu.`is`.pmu.common.AppContainer
|
||||||
import ru.ulstu.`is`.pmu.common.TaskRepository
|
import ru.ulstu.`is`.pmu.common.TaskRepository
|
||||||
import ru.ulstu.`is`.pmu.database.AppDatabase
|
import ru.ulstu.`is`.pmu.database.AppDatabase
|
||||||
import ru.ulstu.`is`.pmu.database.remotekeys.repository.OfflineRemoteKeyRepository
|
import ru.ulstu.`is`.pmu.database.remotekeys.repository.OfflineRemoteKeyRepository
|
||||||
import ru.ulstu.`is`.pmu.database.task.model.Task
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
import ru.ulstu.`is`.pmu.database.task.repository.OfflineTaskRepository
|
import ru.ulstu.`is`.pmu.database.task.repository.OfflineTaskRepository
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
class RestTaskRepository(
|
class RestTaskRepository(
|
||||||
private val service: MyServerService,
|
private val service: MyServerService,
|
||||||
@ -46,6 +50,50 @@ class RestTaskRepository(
|
|||||||
).flow
|
).flow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getAllFavoriteTasks(): Flow<PagingData<Task>> {
|
||||||
|
Log.d(RestTaskRepository::class.simpleName, "Get tasks")
|
||||||
|
|
||||||
|
val pagingSourceFactory = { dbTaskRepository.getAllTasksFavoritePagingSource() }
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
|
return Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
remoteMediator = TaskRemoteMediator(
|
||||||
|
service,
|
||||||
|
dbTaskRepository,
|
||||||
|
dbRemoteKeyRepository,
|
||||||
|
userRestRepository,
|
||||||
|
database,
|
||||||
|
),
|
||||||
|
pagingSourceFactory = pagingSourceFactory
|
||||||
|
).flow
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAllDateTasks(): Flow<PagingData<Task>> {
|
||||||
|
Log.d(RestTaskRepository::class.simpleName, "Get tasks")
|
||||||
|
|
||||||
|
val pagingSourceFactory = { dbTaskRepository.getAllTasksDatePagingSource() }
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagingApi::class)
|
||||||
|
return Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
remoteMediator = TaskRemoteMediator(
|
||||||
|
service,
|
||||||
|
dbTaskRepository,
|
||||||
|
dbRemoteKeyRepository,
|
||||||
|
userRestRepository,
|
||||||
|
database,
|
||||||
|
),
|
||||||
|
pagingSourceFactory = pagingSourceFactory
|
||||||
|
).flow
|
||||||
|
}
|
||||||
|
|
||||||
override suspend fun getTask(uid: Int): Task =
|
override suspend fun getTask(uid: Int): Task =
|
||||||
service.getTask(uid).toTask()
|
service.getTask(uid).toTask()
|
||||||
|
|
||||||
@ -60,4 +108,19 @@ class RestTaskRepository(
|
|||||||
override suspend fun deleteTask(task: Task) {
|
override suspend fun deleteTask(task: Task) {
|
||||||
service.deleteTask(task.uid).toTask()
|
service.deleteTask(task.uid).toTask()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun favoriteTask(task: Task) {
|
||||||
|
task.favorite = true
|
||||||
|
service.updateTask(task.uid, task.toTaskRemote()).toTask()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deletefavoriteTask(task: Task) {
|
||||||
|
task.favorite = false
|
||||||
|
service.updateTask(task.uid, task.toTaskRemote()).toTask()
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun getReport(fromDate: String, toDate: String):List<ReportRemote>
|
||||||
|
{
|
||||||
|
return service.getReportInfo(fromDate,toDate)
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,8 +6,11 @@ import androidx.lifecycle.viewmodel.CreationExtras
|
|||||||
import androidx.lifecycle.viewmodel.initializer
|
import androidx.lifecycle.viewmodel.initializer
|
||||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||||
import ru.ulstu.`is`.pmu.TaskApplication
|
import ru.ulstu.`is`.pmu.TaskApplication
|
||||||
|
import ru.ulstu.`is`.pmu.ui.ReportViewModel
|
||||||
import ru.ulstu.`is`.pmu.ui.task.edit.UserDropDownViewModel
|
import ru.ulstu.`is`.pmu.ui.task.edit.UserDropDownViewModel
|
||||||
import ru.ulstu.`is`.pmu.ui.task.edit.TaskEditViewModel
|
import ru.ulstu.`is`.pmu.ui.task.edit.TaskEditViewModel
|
||||||
|
import ru.ulstu.`is`.pmu.ui.task.list.FavoriteTaskList
|
||||||
|
import ru.ulstu.`is`.pmu.ui.task.list.FavoriteTaskListViewModel
|
||||||
import ru.ulstu.`is`.pmu.ui.task.list.TaskListViewModel
|
import ru.ulstu.`is`.pmu.ui.task.list.TaskListViewModel
|
||||||
|
|
||||||
object AppViewModelProvider {
|
object AppViewModelProvider {
|
||||||
@ -24,6 +27,14 @@ object AppViewModelProvider {
|
|||||||
initializer {
|
initializer {
|
||||||
UserDropDownViewModel(taskApplication().container.userRestRepository)
|
UserDropDownViewModel(taskApplication().container.userRestRepository)
|
||||||
}
|
}
|
||||||
|
initializer {
|
||||||
|
FavoriteTaskListViewModel(taskApplication().container.taskRestRepository)
|
||||||
|
}
|
||||||
|
initializer {
|
||||||
|
ReportViewModel(
|
||||||
|
taskApplication().container.taskRestRepository,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,8 +6,12 @@ import ru.ulstu.`is`.pmu.database.task.model.Task
|
|||||||
|
|
||||||
interface TaskRepository {
|
interface TaskRepository {
|
||||||
fun getAllTasks(): Flow<PagingData<Task>>
|
fun getAllTasks(): Flow<PagingData<Task>>
|
||||||
|
fun getAllFavoriteTasks(): Flow<PagingData<Task>>
|
||||||
|
fun getAllDateTasks(): Flow<PagingData<Task>>
|
||||||
suspend fun getTask(uid: Int): Task
|
suspend fun getTask(uid: Int): Task
|
||||||
suspend fun insertTask(task: Task)
|
suspend fun insertTask(task: Task)
|
||||||
suspend fun updateTask(task: Task)
|
suspend fun updateTask(task: Task)
|
||||||
suspend fun deleteTask(task: Task)
|
suspend fun deleteTask(task: Task)
|
||||||
|
suspend fun favoriteTask(task: Task)
|
||||||
|
suspend fun deletefavoriteTask(task: Task)
|
||||||
}
|
}
|
@ -5,9 +5,11 @@ import androidx.room.PrimaryKey
|
|||||||
import androidx.room.TypeConverter
|
import androidx.room.TypeConverter
|
||||||
import androidx.room.TypeConverters
|
import androidx.room.TypeConverters
|
||||||
import ru.ulstu.`is`.pmu.database.task.model.Task
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
|
import ru.ulstu.`is`.pmu.database.task.model.User
|
||||||
|
|
||||||
enum class RemoteKeyType(private val type: String) {
|
enum class RemoteKeyType(private val type: String) {
|
||||||
STUDENT(Task::class.simpleName ?: "Task");
|
STUDENT(Task::class.simpleName ?: "Task"),
|
||||||
|
USER(User::class.simpleName ?: "User");
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value }
|
fun toRemoteKeyType(value: String) = RemoteKeyType.values().first { it.type == value }
|
||||||
|
@ -14,6 +14,12 @@ interface TaskDao {
|
|||||||
@Query("select * from tasks order by name collate nocase asc")
|
@Query("select * from tasks order by name collate nocase asc")
|
||||||
fun getAll(): PagingSource<Int, Task>
|
fun getAll(): PagingSource<Int, Task>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM tasks WHERE favorite = 1")
|
||||||
|
fun getFavoriteTasks(): PagingSource<Int, Task>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM tasks ORDER BY DATE(SUBSTR(endDate, 7, 4) || '-' || SUBSTR(endDate, 4, 2) || '-' || SUBSTR(endDate, 1, 2)) ASC")
|
||||||
|
fun getTasksSortedByDate(): PagingSource<Int, Task>
|
||||||
|
|
||||||
@Query("select * from tasks where tasks.uid = :uid")
|
@Query("select * from tasks where tasks.uid = :uid")
|
||||||
fun getByUid(uid: Int): Flow<Task>
|
fun getByUid(uid: Int): Flow<Task>
|
||||||
|
|
||||||
@ -26,6 +32,9 @@ interface TaskDao {
|
|||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(task: Task)
|
suspend fun delete(task: Task)
|
||||||
|
|
||||||
|
@Query("UPDATE tasks SET favorite = :favorite WHERE uid = :taskId")
|
||||||
|
suspend fun updateFavorite(taskId: Int, favorite: Boolean)
|
||||||
|
|
||||||
@Query("DELETE FROM tasks")
|
@Query("DELETE FROM tasks")
|
||||||
suspend fun deleteAll()
|
suspend fun deleteAll()
|
||||||
}
|
}
|
@ -1,23 +1,27 @@
|
|||||||
package ru.ulstu.`is`.pmu.database.task.dao
|
package ru.ulstu.`is`.pmu.database.task.dao
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Update
|
import androidx.room.Update
|
||||||
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
import ru.ulstu.`is`.pmu.database.task.model.User
|
import ru.ulstu.`is`.pmu.database.task.model.User
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface UserDao {
|
interface UserDao {
|
||||||
@Query("select * from users order by login collate nocase asc")
|
@Query("select * from users order by login collate nocase asc")
|
||||||
suspend fun getAll(): List<User>
|
suspend fun getAll(): List<User>
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
suspend fun insert(user: User)
|
suspend fun insert(vararg user: User)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
suspend fun update(user: User)
|
suspend fun update(user: User)
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(user: User)
|
suspend fun delete(user: User)
|
||||||
|
|
||||||
|
@Query("DELETE FROM users")
|
||||||
|
suspend fun deleteAll()
|
||||||
}
|
}
|
@ -39,7 +39,7 @@ data class Task(
|
|||||||
endDate: String,
|
endDate: String,
|
||||||
favorite: Boolean,
|
favorite: Boolean,
|
||||||
user: User,
|
user: User,
|
||||||
) : this(0, name, description, endDate, favorite, user.uid)
|
) : this(0, name, description, endDate, favorite, 1)
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -20,6 +20,22 @@ class OfflineTaskRepository(private val taskDao: TaskDao) : TaskRepository {
|
|||||||
pagingSourceFactory = taskDao::getAll
|
pagingSourceFactory = taskDao::getAll
|
||||||
).flow
|
).flow
|
||||||
|
|
||||||
|
override fun getAllFavoriteTasks(): Flow<PagingData<Task>> = Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
pagingSourceFactory = taskDao::getFavoriteTasks
|
||||||
|
).flow
|
||||||
|
|
||||||
|
override fun getAllDateTasks(): Flow<PagingData<Task>> = Pager(
|
||||||
|
config = PagingConfig(
|
||||||
|
pageSize = AppContainer.LIMIT,
|
||||||
|
enablePlaceholders = false
|
||||||
|
),
|
||||||
|
pagingSourceFactory = taskDao::getTasksSortedByDate
|
||||||
|
).flow
|
||||||
|
|
||||||
override suspend fun getTask(uid: Int): Task = taskDao.getByUid(uid).first()
|
override suspend fun getTask(uid: Int): Task = taskDao.getByUid(uid).first()
|
||||||
|
|
||||||
override suspend fun insertTask(task: Task) = taskDao.insert(task)
|
override suspend fun insertTask(task: Task) = taskDao.insert(task)
|
||||||
@ -27,12 +43,21 @@ class OfflineTaskRepository(private val taskDao: TaskDao) : TaskRepository {
|
|||||||
override suspend fun updateTask(task: Task) = taskDao.update(task)
|
override suspend fun updateTask(task: Task) = taskDao.update(task)
|
||||||
|
|
||||||
override suspend fun deleteTask(task: Task) = taskDao.delete(task)
|
override suspend fun deleteTask(task: Task) = taskDao.delete(task)
|
||||||
|
|
||||||
override suspend fun favoriteTask(task: Task) {
|
override suspend fun favoriteTask(task: Task) {
|
||||||
TODO("Not yet implemented")
|
taskDao.updateFavorite(task.uid, !task.favorite)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun deletefavoriteTask(task: Task) {
|
||||||
|
taskDao.updateFavorite(task.uid, !task.favorite)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAllTasksPagingSource(): PagingSource<Int, Task> = taskDao.getAll()
|
fun getAllTasksPagingSource(): PagingSource<Int, Task> = taskDao.getAll()
|
||||||
|
|
||||||
|
fun getAllTasksFavoritePagingSource(): PagingSource<Int, Task> = taskDao.getFavoriteTasks()
|
||||||
|
|
||||||
|
fun getAllTasksDatePagingSource(): PagingSource<Int, Task> = taskDao.getTasksSortedByDate()
|
||||||
|
|
||||||
suspend fun insertTasks(tasks: List<Task>) =
|
suspend fun insertTasks(tasks: List<Task>) =
|
||||||
taskDao.insert(*tasks.toTypedArray())
|
taskDao.insert(*tasks.toTypedArray())
|
||||||
|
|
||||||
|
@ -1,11 +1,23 @@
|
|||||||
package ru.ulstu.`is`.pmu.database.task.repository
|
package ru.ulstu.`is`.pmu.database.task.repository
|
||||||
|
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import ru.ulstu.`is`.pmu.common.AppContainer
|
||||||
import ru.ulstu.`is`.pmu.common.UserRepository
|
import ru.ulstu.`is`.pmu.common.UserRepository
|
||||||
import ru.ulstu.`is`.pmu.database.task.dao.UserDao
|
import ru.ulstu.`is`.pmu.database.task.dao.UserDao
|
||||||
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
import ru.ulstu.`is`.pmu.database.task.model.User
|
import ru.ulstu.`is`.pmu.database.task.model.User
|
||||||
|
|
||||||
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
|
class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
|
||||||
override suspend fun getAllUsers(): List<User> = userDao.getAll()
|
override suspend fun getAllUsers(): List<User> = userDao.getAll()
|
||||||
|
|
||||||
|
suspend fun insertUsers(users: List<User>) =
|
||||||
|
userDao.insert(*users.toTypedArray())
|
||||||
|
|
||||||
suspend fun createUser(user: User) = userDao.insert(user)
|
suspend fun createUser(user: User) = userDao.insert(user)
|
||||||
suspend fun updateUser(user: User) = userDao.update(user)
|
suspend fun updateUser(user: User) = userDao.update(user)
|
||||||
|
suspend fun clearUsers() = userDao.deleteAll()
|
||||||
}
|
}
|
@ -1,4 +1,362 @@
|
|||||||
package ru.ulstu.is.pmu.ui
|
package ru.ulstu.`is`.pmu.ui
|
||||||
|
|
||||||
class ReportPage {
|
import android.content.ContentResolver
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.graphics.pdf.PdfDocument
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Environment
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import android.util.Log
|
||||||
|
import android.widget.CalendarView
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.RowScope
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.DatePicker
|
||||||
|
import androidx.compose.material3.DatePickerState
|
||||||
|
import androidx.compose.material3.DisplayMode
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.OutlinedTextField
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.rememberDatePickerState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import ru.ulstu.`is`.pmu.R
|
||||||
|
import ru.ulstu.`is`.pmu.api.report.ReportRemote
|
||||||
|
import ru.ulstu.`is`.pmu.common.AppViewModelProvider
|
||||||
|
import ru.ulstu.`is`.pmu.ui.ReportViewModel
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun ReportPage(navController: NavController?, viewModel: ReportViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
|
var showStartDatePicker by remember { mutableStateOf(false) }
|
||||||
|
var showEndDatePicker by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.verticalScroll(rememberScrollState()),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Button(
|
||||||
|
onClick = { showStartDatePicker = true },
|
||||||
|
modifier = Modifier.width(350.dp)
|
||||||
|
) {
|
||||||
|
Text("Начальная дата")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (showStartDatePicker) {
|
||||||
|
DatePicker(
|
||||||
|
onDateSelected = { selectedDate ->
|
||||||
|
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(startDate = selectedDate))
|
||||||
|
showStartDatePicker = false
|
||||||
|
},
|
||||||
|
onDismissRequest = { showStartDatePicker = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = { showEndDatePicker = true },
|
||||||
|
modifier = Modifier.width(350.dp)
|
||||||
|
) {
|
||||||
|
Text("Конечная дата")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showEndDatePicker) {
|
||||||
|
DatePicker(
|
||||||
|
onDateSelected = { selectedDate ->
|
||||||
|
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(endDate = selectedDate))
|
||||||
|
showEndDatePicker = false
|
||||||
|
if (viewModel.reportPageUiState.reportDetails.startDate != null) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
viewModel.getReport()
|
||||||
|
createPdfFile(context, "Отчет.pdf", viewModel.reportResultPageUiState.resReport)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDismissRequest = { showEndDatePicker = false }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
Text(
|
||||||
|
text = "Результат",
|
||||||
|
style = MaterialTheme.typography.headlineLarge
|
||||||
|
)
|
||||||
|
|
||||||
|
ReportTable(reportData = viewModel.reportResultPageUiState.resReport)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ReportTable(reportData: List<ReportRemote>) {
|
||||||
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
|
reportData.forEach { report ->
|
||||||
|
ReportCard(report)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ReportCard(report: ReportRemote) {
|
||||||
|
val (taskId, taskName, taskDescription, taskEndDate) = report
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) // Исправлено здесь
|
||||||
|
) {
|
||||||
|
Column(modifier = Modifier.padding(16.dp)) {
|
||||||
|
Text("ID: ${taskId}", style = MaterialTheme.typography.bodyMedium)
|
||||||
|
Text("Название: $taskName", style = MaterialTheme.typography.bodyMedium)
|
||||||
|
Text("Описание: $taskDescription", style = MaterialTheme.typography.bodyMedium)
|
||||||
|
Text("Дата завершения: $taskEndDate", style = MaterialTheme.typography.bodyMedium)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CustomCalendarView(onDateSelected: (Date) -> Unit) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = Modifier.wrapContentSize(),
|
||||||
|
factory = { context ->
|
||||||
|
val calendarView = CalendarView(context)
|
||||||
|
|
||||||
|
// Настройки CalendarView
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
|
||||||
|
calendarView.setOnDateChangeListener { _, year, month, dayOfMonth ->
|
||||||
|
calendar.set(Calendar.YEAR, year)
|
||||||
|
calendar.set(Calendar.MONTH, month)
|
||||||
|
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
|
||||||
|
onDateSelected(calendar.time)
|
||||||
|
}
|
||||||
|
|
||||||
|
calendarView
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DatePicker(onDateSelected: (Date) -> Unit, onDismissRequest: () -> Unit) {
|
||||||
|
val selDate = remember { mutableStateOf(Date()) }
|
||||||
|
|
||||||
|
//todo - add strings to resource after POC
|
||||||
|
Dialog(onDismissRequest = { onDismissRequest() }, properties = DialogProperties()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.wrapContentSize()
|
||||||
|
.background(
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.defaultMinSize(minHeight = 72.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Выберите дату"
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(24.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = SimpleDateFormat("MMM d, yyyy", Locale.getDefault()).format(selDate.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomCalendarView(onDateSelected = {
|
||||||
|
selDate.value = it
|
||||||
|
})
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.End)
|
||||||
|
.padding(bottom = 16.dp, end = 16.dp)
|
||||||
|
.background(
|
||||||
|
color = Color.White,
|
||||||
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = onDismissRequest
|
||||||
|
) {
|
||||||
|
//TODO - hardcode string
|
||||||
|
Text(
|
||||||
|
text = "Отмена"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onDateSelected(selDate.value)
|
||||||
|
onDismissRequest()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
//TODO - hardcode string
|
||||||
|
Text(
|
||||||
|
text = "Подтвердить"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun createPdfFile(context: Context, fileName: String, reportData: List<ReportRemote>) {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val contentValues = ContentValues().apply {
|
||||||
|
put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
|
||||||
|
put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
|
||||||
|
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS)
|
||||||
|
}
|
||||||
|
|
||||||
|
val contentResolver: ContentResolver = context.contentResolver
|
||||||
|
val uri: Uri? = contentResolver.insert(MediaStore.Files.getContentUri("external"), contentValues)
|
||||||
|
|
||||||
|
uri?.let {
|
||||||
|
try {
|
||||||
|
contentResolver.openOutputStream(uri)?.use { outputStream ->
|
||||||
|
createPdfContent(outputStream, reportData)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri))
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createPdfContent(outputStream: OutputStream, reportData: List<ReportRemote>) {
|
||||||
|
val pdfDocument = PdfDocument()
|
||||||
|
|
||||||
|
val pageInfo = PdfDocument.PageInfo.Builder(595, 842, 1).create()
|
||||||
|
val page = pdfDocument.startPage(pageInfo)
|
||||||
|
val canvas = page.canvas
|
||||||
|
|
||||||
|
val paint = Paint()
|
||||||
|
paint.color = android.graphics.Color.BLACK
|
||||||
|
paint.textSize = 24f
|
||||||
|
|
||||||
|
// Отображаем заголовок
|
||||||
|
val title = "Отчет"
|
||||||
|
val xTitle = (pageInfo.pageWidth - paint.measureText(title)) / 2
|
||||||
|
val yTitle = 40f
|
||||||
|
canvas.drawText(title, xTitle, yTitle, paint)
|
||||||
|
|
||||||
|
// Определение структуры таблицы
|
||||||
|
val tableWidth = pageInfo.pageWidth - 80f
|
||||||
|
val columnCount = 4
|
||||||
|
val columnWidth = tableWidth / columnCount
|
||||||
|
val rowHeight = 40f
|
||||||
|
|
||||||
|
// Отображаем заголовки столбцов
|
||||||
|
val columnHeaderPaint = Paint()
|
||||||
|
columnHeaderPaint.color = android.graphics.Color.BLACK
|
||||||
|
columnHeaderPaint.textSize = 18f
|
||||||
|
val columnHeaderTitles = listOf("ID", "Название", "Описание", "Дата окончания")
|
||||||
|
for (i in 0 until columnCount) {
|
||||||
|
val x = 40f + i * columnWidth
|
||||||
|
val y = yTitle + 80f
|
||||||
|
canvas.drawText(columnHeaderTitles[i], x, y, columnHeaderPaint)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отображаем данные в таблице
|
||||||
|
val rowDataPaint = Paint()
|
||||||
|
rowDataPaint.color = android.graphics.Color.BLACK
|
||||||
|
rowDataPaint.textSize = 18f
|
||||||
|
for ((index, report) in reportData.withIndex()) {
|
||||||
|
val x = 40f
|
||||||
|
val y = yTitle + 120f + index * rowHeight
|
||||||
|
|
||||||
|
// Отображаем данные каждого столбца
|
||||||
|
canvas.drawText(report.id.toString(), x, y, rowDataPaint)
|
||||||
|
canvas.drawText(report.name, x + columnWidth, y, rowDataPaint)
|
||||||
|
canvas.drawText(report.description, x + 2 * columnWidth, y, rowDataPaint)
|
||||||
|
canvas.drawText(report.endDate, x + 3 * columnWidth, y, rowDataPaint)
|
||||||
|
}
|
||||||
|
|
||||||
|
pdfDocument.finishPage(page)
|
||||||
|
|
||||||
|
pdfDocument.writeTo(outputStream)
|
||||||
|
pdfDocument.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun drawCell(canvas: Canvas, paint: Paint, text: String, x: Float, y: Float, width: Int) {
|
||||||
|
canvas.drawText(text, x + (width - paint.measureText(text)) / 2, y + paint.textSize, paint)
|
||||||
}
|
}
|
@ -9,8 +9,10 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import ru.ulstu.`is`.pmu.api.model.TaskRemote
|
import ru.ulstu.`is`.pmu.api.model.TaskRemote
|
||||||
|
import ru.ulstu.`is`.pmu.api.model.toTask
|
||||||
import ru.ulstu.`is`.pmu.api.report.ReportRemote
|
import ru.ulstu.`is`.pmu.api.report.ReportRemote
|
||||||
import ru.ulstu.`is`.pmu.api.task.RestTaskRepository
|
import ru.ulstu.`is`.pmu.api.task.RestTaskRepository
|
||||||
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -29,8 +29,10 @@ import androidx.navigation.compose.currentBackStackEntryAsState
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
import ru.ulstu.`is`.pmu.R
|
import ru.ulstu.`is`.pmu.R
|
||||||
|
import ru.ulstu.`is`.pmu.ui.ReportPage
|
||||||
import ru.ulstu.`is`.pmu.ui.about.About
|
import ru.ulstu.`is`.pmu.ui.about.About
|
||||||
import ru.ulstu.`is`.pmu.ui.task.edit.TaskEdit
|
import ru.ulstu.`is`.pmu.ui.task.edit.TaskEdit
|
||||||
|
import ru.ulstu.`is`.pmu.ui.task.list.FavoriteTaskList
|
||||||
import ru.ulstu.`is`.pmu.ui.task.list.TaskList
|
import ru.ulstu.`is`.pmu.ui.task.list.TaskList
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@ -109,6 +111,8 @@ fun Navhost(
|
|||||||
) {
|
) {
|
||||||
TaskEdit(navController)
|
TaskEdit(navController)
|
||||||
}
|
}
|
||||||
|
composable(Screen.TaskFavoriteList.route) { FavoriteTaskList(navController) }
|
||||||
|
composable(Screen.Report.route) { ReportPage(navController = navController) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.Favorite
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
import androidx.compose.material.icons.filled.List
|
import androidx.compose.material.icons.filled.List
|
||||||
|
import androidx.compose.material.icons.filled.Send
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import ru.ulstu.`is`.pmu.R
|
import ru.ulstu.`is`.pmu.R
|
||||||
|
|
||||||
@ -20,6 +21,12 @@ enum class Screen(
|
|||||||
About(
|
About(
|
||||||
"about", R.string.about_title, Icons.Filled.Info
|
"about", R.string.about_title, Icons.Filled.Info
|
||||||
),
|
),
|
||||||
|
TaskFavoriteList(
|
||||||
|
"task-favorite", R.string.task_favorite_view_title
|
||||||
|
),
|
||||||
|
Report(
|
||||||
|
"report", R.string.report, Icons.Filled.Send
|
||||||
|
),
|
||||||
TaskEdit(
|
TaskEdit(
|
||||||
"task-edit/{id}", R.string.task_view_title, showInBottomBar = false
|
"task-edit/{id}", R.string.task_view_title, showInBottomBar = false
|
||||||
);
|
);
|
||||||
@ -27,7 +34,9 @@ enum class Screen(
|
|||||||
companion object {
|
companion object {
|
||||||
val bottomBarItems = listOf(
|
val bottomBarItems = listOf(
|
||||||
TaskList,
|
TaskList,
|
||||||
|
TaskFavoriteList,
|
||||||
About,
|
About,
|
||||||
|
Report
|
||||||
)
|
)
|
||||||
|
|
||||||
fun getItem(route: String): Screen? {
|
fun getItem(route: String): Screen? {
|
||||||
|
@ -1,11 +1,19 @@
|
|||||||
package ru.ulstu.`is`.pmu.ui.task.edit
|
package ru.ulstu.`is`.pmu.ui.task.edit
|
||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.widget.CalendarView
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.wrapContentSize
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
@ -16,6 +24,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -23,12 +32,16 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.compose.ui.window.Dialog
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -37,6 +50,10 @@ import ru.ulstu.`is`.pmu.database.task.model.User
|
|||||||
import ru.ulstu.`is`.pmu.database.task.model.Task
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
import ru.ulstu.`is`.pmu.common.AppViewModelProvider
|
import ru.ulstu.`is`.pmu.common.AppViewModelProvider
|
||||||
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Calendar
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TaskEdit(
|
fun TaskEdit(
|
||||||
@ -44,17 +61,14 @@ fun TaskEdit(
|
|||||||
viewModel: TaskEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
viewModel: TaskEditViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||||
userViewModel: UserDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
userViewModel: UserDropDownViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
userViewModel.setCurrentUser(viewModel.taskUiState.taskDetails.userId)
|
userViewModel.setCurrentUser(viewModel.taskUiState.taskDetails.userId)
|
||||||
TaskEdit(
|
TaskEdit(
|
||||||
taskUiState = viewModel.taskUiState,
|
taskUiState = viewModel.taskUiState,
|
||||||
userUiState = userViewModel.userUiState,
|
userUiState = userViewModel.userUiState,
|
||||||
usersListUiState = userViewModel.usersListUiState,
|
usersListUiState = userViewModel.usersListUiState,
|
||||||
onClick = {
|
onClick = {
|
||||||
coroutineScope.launch {
|
|
||||||
viewModel.saveTask()
|
viewModel.saveTask()
|
||||||
navController.popBackStack()
|
navController.popBackStack()
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onUpdate = viewModel::updateUiState,
|
onUpdate = viewModel::updateUiState,
|
||||||
onUserUpdate = userViewModel::updateUiState
|
onUserUpdate = userViewModel::updateUiState
|
||||||
@ -121,6 +135,7 @@ private fun TaskEdit(
|
|||||||
onUpdate: (TaskDetails) -> Unit,
|
onUpdate: (TaskDetails) -> Unit,
|
||||||
onUserUpdate: (User) -> Unit
|
onUserUpdate: (User) -> Unit
|
||||||
) {
|
) {
|
||||||
|
var showInvalidDateDialog by remember { mutableStateOf(false) }
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -140,48 +155,166 @@ private fun TaskEdit(
|
|||||||
label = { Text(stringResource(id = R.string.task_lastname)) },
|
label = { Text(stringResource(id = R.string.task_lastname)) },
|
||||||
singleLine = true
|
singleLine = true
|
||||||
)
|
)
|
||||||
UserDropDown(
|
var showDatePicker by remember { mutableStateOf(false) }
|
||||||
userUiState = userUiState,
|
if (showDatePicker) {
|
||||||
usersListUiState = usersListUiState,
|
DatePicker(
|
||||||
onUserUpdate = {
|
onDateSelected = { selectedDate ->
|
||||||
onUpdate(taskUiState.taskDetails.copy(userId = it.uid))
|
onUpdate(taskUiState.taskDetails.copy(endDate = SimpleDateFormat("dd.MM.yyyy").format(selectedDate)))
|
||||||
onUserUpdate(it)
|
showDatePicker = false
|
||||||
|
},
|
||||||
|
onDismissRequest = {
|
||||||
|
showDatePicker = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Button(
|
||||||
|
onClick = { showDatePicker = true },
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
Text("Выбрать дату окончания")
|
||||||
|
}
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
value = taskUiState.taskDetails.endDate,
|
value = taskUiState.taskDetails.endDate,
|
||||||
onValueChange = { onUpdate(taskUiState.taskDetails.copy(endDate = it)) },
|
onValueChange = { onUpdate(taskUiState.taskDetails.copy(endDate = it)) },
|
||||||
label = { Text(stringResource(id = R.string.task_phone)) },
|
label = { Text(stringResource(id = R.string.task_phone)) },
|
||||||
singleLine = true
|
singleLine = true ,
|
||||||
|
enabled = false
|
||||||
)
|
)
|
||||||
Button(
|
Button(
|
||||||
onClick = onClick,
|
onClick = {
|
||||||
|
if (!isValidDate(taskUiState.taskDetails.endDate)) {
|
||||||
|
showInvalidDateDialog = true
|
||||||
|
} else {
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
},
|
||||||
enabled = taskUiState.isEntryValid,
|
enabled = taskUiState.isEntryValid,
|
||||||
shape = MaterialTheme.shapes.small,
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
Text(text = stringResource(R.string.task_save_button))
|
Text(text = stringResource(R.string.task_save_button))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Preview(name = "Light Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_NO)
|
if (showInvalidDateDialog) {
|
||||||
@Preview(name = "Dark Mode", showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
AlertDialog(
|
||||||
@Composable
|
onDismissRequest = { showInvalidDateDialog = false },
|
||||||
fun TaskEditPreview() {
|
title = { Text("Неверный формат даты") },
|
||||||
PmudemoTheme {
|
text = { Text("Введите дату по шаблону: 01.12.2023") },
|
||||||
Surface(
|
confirmButton = {
|
||||||
color = MaterialTheme.colorScheme.background
|
Button(onClick = { showInvalidDateDialog = false }) {
|
||||||
) {
|
Text("Подтвердить")
|
||||||
TaskEdit(
|
}
|
||||||
taskUiState = Task.getTask().toUiState(true),
|
}
|
||||||
userUiState = User.DEMO_User.toUiState(),
|
|
||||||
usersListUiState = UsersListUiState(listOf()),
|
|
||||||
onClick = {},
|
|
||||||
onUpdate = {},
|
|
||||||
onUserUpdate = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isValidDate(date: String): Boolean {
|
||||||
|
val regex = Regex("""^\d{2}\.\d{2}\.\d{4}$""")
|
||||||
|
return regex.matches(date)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CustomCalendarView(onDateSelected: (Date) -> Unit) {
|
||||||
|
AndroidView(
|
||||||
|
modifier = Modifier.wrapContentSize(),
|
||||||
|
factory = { context ->
|
||||||
|
val calendarView = CalendarView(context)
|
||||||
|
|
||||||
|
// Настройки CalendarView
|
||||||
|
val calendar = Calendar.getInstance()
|
||||||
|
|
||||||
|
// Устанавливаем минимальную дату выбора на сегодняшний день
|
||||||
|
calendarView.minDate = calendar.timeInMillis
|
||||||
|
|
||||||
|
calendarView.setOnDateChangeListener { _, year, month, dayOfMonth ->
|
||||||
|
calendar.set(Calendar.YEAR, year)
|
||||||
|
calendar.set(Calendar.MONTH, month)
|
||||||
|
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
|
||||||
|
onDateSelected(calendar.time)
|
||||||
|
}
|
||||||
|
|
||||||
|
calendarView
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DatePicker(onDateSelected: (Date) -> Unit, onDismissRequest: () -> Unit) {
|
||||||
|
val selDate = remember { mutableStateOf(Date()) }
|
||||||
|
|
||||||
|
//todo - add strings to resource after POC
|
||||||
|
Dialog(onDismissRequest = { onDismissRequest() }, properties = DialogProperties()) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.wrapContentSize()
|
||||||
|
.background(
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.defaultMinSize(minHeight = 72.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
color = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Выберите дату"
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(24.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = SimpleDateFormat("MMM d, yyyy", Locale.getDefault()).format(selDate.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(16.dp))
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomCalendarView(onDateSelected = {
|
||||||
|
selDate.value = it
|
||||||
|
})
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.size(8.dp))
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.End)
|
||||||
|
.padding(bottom = 16.dp, end = 16.dp)
|
||||||
|
.background(
|
||||||
|
color = Color.White,
|
||||||
|
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = onDismissRequest
|
||||||
|
) {
|
||||||
|
//TODO - hardcode string
|
||||||
|
Text(
|
||||||
|
text = "Отмена"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onDateSelected(selDate.value)
|
||||||
|
onDismissRequest()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
//TODO - hardcode string
|
||||||
|
Text(
|
||||||
|
text = "Подтвердить"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -7,13 +7,14 @@ import androidx.lifecycle.SavedStateHandle
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.ulstu.`is`.pmu.common.MyViewModel
|
||||||
import ru.ulstu.`is`.pmu.common.TaskRepository
|
import ru.ulstu.`is`.pmu.common.TaskRepository
|
||||||
import ru.ulstu.`is`.pmu.database.task.model.Task
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
|
|
||||||
class TaskEditViewModel(
|
class TaskEditViewModel(
|
||||||
savedStateHandle: SavedStateHandle,
|
savedStateHandle: SavedStateHandle,
|
||||||
private val taskRepository: TaskRepository
|
private val taskRepository: TaskRepository
|
||||||
) : ViewModel() {
|
) : MyViewModel() {
|
||||||
|
|
||||||
var taskUiState by mutableStateOf(TaskUiState())
|
var taskUiState by mutableStateOf(TaskUiState())
|
||||||
private set
|
private set
|
||||||
@ -21,11 +22,16 @@ class TaskEditViewModel(
|
|||||||
private val taskUid: Int = checkNotNull(savedStateHandle["id"])
|
private val taskUid: Int = checkNotNull(savedStateHandle["id"])
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
|
||||||
if (taskUid > 0) {
|
if (taskUid > 0) {
|
||||||
|
runInScope(
|
||||||
|
actionSuccess = {
|
||||||
taskUiState = taskRepository.getTask(taskUid)
|
taskUiState = taskRepository.getTask(taskUid)
|
||||||
.toUiState(true)
|
.toUiState(true)
|
||||||
|
},
|
||||||
|
actionError = {
|
||||||
|
taskUiState = TaskUiState()
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,10 +41,9 @@ class TaskEditViewModel(
|
|||||||
isEntryValid = validateInput(taskDetails)
|
isEntryValid = validateInput(taskDetails)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
fun saveTask() {
|
||||||
suspend fun saveTask() {
|
|
||||||
if (validateInput()) {
|
if (validateInput()) {
|
||||||
if (taskUid > 0) {
|
runInScope ( actionSuccess = { if (taskUid > 0) {
|
||||||
taskRepository.updateTask(
|
taskRepository.updateTask(
|
||||||
taskUiState.taskDetails.toTask(taskUid)
|
taskUiState.taskDetails.toTask(taskUid)
|
||||||
)
|
)
|
||||||
@ -47,6 +52,7 @@ class TaskEditViewModel(
|
|||||||
taskUiState.taskDetails.toTask()
|
taskUiState.taskDetails.toTask()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,7 +61,6 @@ class TaskEditViewModel(
|
|||||||
name.isNotBlank()
|
name.isNotBlank()
|
||||||
&& description.isNotBlank()
|
&& description.isNotBlank()
|
||||||
&& endDate.isNotBlank()
|
&& endDate.isNotBlank()
|
||||||
&& userId > 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,176 @@
|
|||||||
package ru.ulstu.is.pmu.ui.student.list
|
package ru.ulstu.`is`.pmu.ui.task.list
|
||||||
|
|
||||||
class FavoriteTaskList {
|
import android.content.res.Configuration
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.Edit
|
||||||
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
|
import androidx.compose.material3.DismissDirection
|
||||||
|
import androidx.compose.material3.DismissState
|
||||||
|
import androidx.compose.material3.DismissValue
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FloatingActionButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.SwipeToDismiss
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.rememberDismissState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.zIndex
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import androidx.navigation.NavController
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import androidx.paging.compose.LazyPagingItems
|
||||||
|
import androidx.paging.compose.collectAsLazyPagingItems
|
||||||
|
import androidx.paging.compose.itemContentType
|
||||||
|
import androidx.paging.compose.itemKey
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.ulstu.`is`.pmu.common.AppViewModelProvider
|
||||||
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
|
import ru.ulstu.`is`.pmu.ui.navigation.Screen
|
||||||
|
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun FavoriteTaskList(
|
||||||
|
navController: NavController,
|
||||||
|
viewModel: FavoriteTaskListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
|
) {
|
||||||
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val taskListUiState = viewModel.taskListUiState.collectAsLazyPagingItems()
|
||||||
|
Scaffold(
|
||||||
|
topBar = {}
|
||||||
|
) { innerPadding ->
|
||||||
|
FavoriteTaskList(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(innerPadding)
|
||||||
|
.fillMaxSize(),
|
||||||
|
taskList = taskListUiState
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun SwipeToDelete(
|
||||||
|
task: Task
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = 7.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = String.format("%s%n%s%n%s", task.name, task.description, task.endDate),
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun FavoriteTaskList(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
taskList: LazyPagingItems<Task>
|
||||||
|
) {
|
||||||
|
val refreshScope = rememberCoroutineScope()
|
||||||
|
var refreshing by remember { mutableStateOf(false) }
|
||||||
|
fun refresh() = refreshScope.launch {
|
||||||
|
refreshing = true
|
||||||
|
taskList.refresh()
|
||||||
|
refreshing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
val state = rememberPullRefreshState(refreshing, ::refresh)
|
||||||
|
Box(
|
||||||
|
modifier = modifier.pullRefresh(state)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
|
||||||
|
items(
|
||||||
|
count = taskList.itemCount,
|
||||||
|
key = taskList.itemKey(),
|
||||||
|
contentType = taskList.itemContentType()
|
||||||
|
) { index ->
|
||||||
|
val task = taskList[index]
|
||||||
|
task?.let {
|
||||||
|
SwipeToDelete(
|
||||||
|
task = task
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PullRefreshIndicator(
|
||||||
|
refreshing, state,
|
||||||
|
Modifier
|
||||||
|
.align(CenterHorizontally)
|
||||||
|
.zIndex(100f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun TaskListItem(
|
||||||
|
task: Task, modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
Card(
|
||||||
|
modifier = modifier.fillMaxWidth(),
|
||||||
|
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier.padding(all = 10.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = String.format("%s %s", task.name, task.description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,4 +1,15 @@
|
|||||||
package ru.ulstu.is.pmu.ui.student.list
|
package ru.ulstu.`is`.pmu.ui.task.list
|
||||||
|
|
||||||
class FavoriteTaskListViewModel {
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.paging.PagingData
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import ru.ulstu.`is`.pmu.common.MyViewModel
|
||||||
|
import ru.ulstu.`is`.pmu.common.TaskRepository
|
||||||
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
|
|
||||||
|
class FavoriteTaskListViewModel(
|
||||||
|
private val taskRepository: TaskRepository
|
||||||
|
) : MyViewModel() {
|
||||||
|
|
||||||
|
val taskListUiState: Flow<PagingData<Task>> = taskRepository.getAllFavoriteTasks()
|
||||||
}
|
}
|
@ -16,11 +16,13 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.ExperimentalMaterialApi
|
import androidx.compose.material.ExperimentalMaterialApi
|
||||||
import androidx.compose.material.IconButton
|
import androidx.compose.material.IconButton
|
||||||
|
import androidx.compose.material.TextButton
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
import androidx.compose.material.icons.filled.Favorite
|
import androidx.compose.material.icons.filled.Favorite
|
||||||
|
import androidx.compose.material.icons.filled.FavoriteBorder
|
||||||
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
import androidx.compose.material.pullrefresh.PullRefreshIndicator
|
||||||
import androidx.compose.material.pullrefresh.pullRefresh
|
import androidx.compose.material.pullrefresh.pullRefresh
|
||||||
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
import androidx.compose.material.pullrefresh.rememberPullRefreshState
|
||||||
@ -66,13 +68,20 @@ import ru.ulstu.`is`.pmu.common.AppViewModelProvider
|
|||||||
import ru.ulstu.`is`.pmu.database.task.model.Task
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
import ru.ulstu.`is`.pmu.ui.navigation.Screen
|
import ru.ulstu.`is`.pmu.ui.navigation.Screen
|
||||||
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
import ru.ulstu.`is`.pmu.ui.theme.PmudemoTheme
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
|
import androidx.compose.ui.text.withStyle
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TaskList(
|
fun TaskList(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
viewModel: TaskListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
viewModel: TaskListViewModel = viewModel(factory = AppViewModelProvider.Factory)
|
||||||
) {
|
) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
val taskListUiState = viewModel.taskListUiState.collectAsLazyPagingItems()
|
val taskListUiState = viewModel.taskListUiState.collectAsLazyPagingItems()
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {},
|
topBar = {},
|
||||||
@ -92,21 +101,15 @@ fun TaskList(
|
|||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
taskList = taskListUiState,
|
taskList = taskListUiState,
|
||||||
onClick = { uid: Int ->
|
|
||||||
val route = Screen.TaskEdit.route.replace("{id}", uid.toString())
|
|
||||||
navController.navigate(route)
|
|
||||||
},
|
|
||||||
onDeleteClick = { task: Task ->
|
onDeleteClick = { task: Task ->
|
||||||
// Обработка удаления задачи
|
// Обработка удаления задачи
|
||||||
coroutineScope.launch {
|
|
||||||
viewModel.deleteTask(task)
|
viewModel.deleteTask(task)
|
||||||
}
|
taskListUiState.refresh()
|
||||||
},
|
},
|
||||||
onAddToFavoritesClick = { task: Task ->
|
onAddToFavoritesClick = { task: Task ->
|
||||||
// Обработка добавления задачи в избранное
|
// Обработка добавления задачи в избранное
|
||||||
coroutineScope.launch {
|
if(task.favorite == false) { viewModel.favoriteTask(task) } else viewModel.deletefavoriteTask(task)
|
||||||
viewModel.favoriteTask(task)
|
taskListUiState.refresh()
|
||||||
}
|
|
||||||
},
|
},
|
||||||
onEditClick = { task: Task ->
|
onEditClick = { task: Task ->
|
||||||
// Обработка редактирования задачи
|
// Обработка редактирования задачи
|
||||||
@ -145,15 +148,66 @@ fun DismissBackground(dismissState: DismissState) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun calculateDaysDifference(endDate: Date, currentDate: Date): Int {
|
||||||
|
try {
|
||||||
|
val timeDifference = endDate.time - currentDate.time
|
||||||
|
return (timeDifference / (24 * 60 * 60 * 1000)).toInt()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun SwipeToDelete(
|
private fun SwipeToDelete(
|
||||||
task: Task,
|
task: Task,
|
||||||
onClick: (uid: Int) -> Unit,
|
|
||||||
onDeleteClick: (task: Task) -> Unit,
|
onDeleteClick: (task: Task) -> Unit,
|
||||||
onAddToFavoritesClick: (task: Task) -> Unit,
|
onAddToFavoritesClick: (task: Task) -> Unit,
|
||||||
onEditClick: (task: Task) -> Unit
|
onEditClick: (task: Task) -> Unit
|
||||||
) {
|
) {
|
||||||
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
// Добавляем диалоговое окно для подтверждения удаления задачи
|
||||||
|
if (showDialog) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.Black.copy(alpha = 0.5f)) // Меняем уровень прозрачности и цвет
|
||||||
|
) {
|
||||||
|
// Диалоговое окно
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showDialog = false },
|
||||||
|
title = {
|
||||||
|
Text(text = "Удаление задачи")
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = "Вы уверены, что хотите удалить эту задачу?")
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onDeleteClick(task)
|
||||||
|
showDialog =
|
||||||
|
false // Закрываем диалоговое окно после подтверждения удаления
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = "Да")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
showDialog = false // Закрываем диалоговое окно без удаления задачи
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(text = "Отмена")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -165,8 +219,35 @@ private fun SwipeToDelete(
|
|||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
|
val currentDateString = SimpleDateFormat("dd.MM.yyyy").format(Date())
|
||||||
|
val currentDate = SimpleDateFormat("dd.MM.yyyy").parse(currentDateString)
|
||||||
|
|
||||||
|
val taskName = task.name
|
||||||
|
val taskDescription = task.description
|
||||||
|
val endDate = SimpleDateFormat("dd.MM.yyyy").parse(task.endDate)
|
||||||
|
|
||||||
|
val isOverdue = currentDate.after(endDate)
|
||||||
|
val overdueText = if (isOverdue) {
|
||||||
|
val daysDifference = calculateDaysDifference(currentDate, endDate)
|
||||||
|
val daysWord = if (daysDifference < 23) "дня" else "дней"
|
||||||
|
"Задача просрочена на $daysDifference $daysWord"
|
||||||
|
} else {
|
||||||
|
task.endDate
|
||||||
|
}
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = String.format("%s %s", task.name, task.description),
|
text = buildAnnotatedString {
|
||||||
|
withStyle(style = SpanStyle(color = Color.Black)) {
|
||||||
|
append("$taskName\n$taskDescription\n")
|
||||||
|
}
|
||||||
|
if (isOverdue) {
|
||||||
|
withStyle(style = SpanStyle(color = Color.Red)) {
|
||||||
|
append(overdueText)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
append(overdueText)
|
||||||
|
}
|
||||||
|
},
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -177,18 +258,18 @@ private fun SwipeToDelete(
|
|||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Edit,
|
imageVector = Icons.Default.Edit,
|
||||||
contentDescription = "Редактировать",
|
contentDescription = "Редактировать",
|
||||||
tint = Color.Blue
|
tint = Color.Black
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { onDeleteClick(task) },
|
onClick = { showDialog = true }, // Показываем диалоговое окно при нажатии на кнопку удаления
|
||||||
modifier = Modifier.padding(start = 8.dp)
|
modifier = Modifier.padding(start = 8.dp)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Delete,
|
imageVector = Icons.Default.Delete,
|
||||||
contentDescription = "Удалить",
|
contentDescription = "Удалить",
|
||||||
tint = Color.Red
|
tint = Color.Black
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,8 +278,9 @@ private fun SwipeToDelete(
|
|||||||
modifier = Modifier.padding(start = 8.dp)
|
modifier = Modifier.padding(start = 8.dp)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Favorite,
|
imageVector = if (task.favorite) Icons.Default.Favorite else Icons.Default.FavoriteBorder,
|
||||||
contentDescription = "Добавить в избранное"
|
contentDescription = "Добавить в избранное",
|
||||||
|
tint = Color.Black
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -210,7 +292,6 @@ private fun SwipeToDelete(
|
|||||||
private fun TaskList(
|
private fun TaskList(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
taskList: LazyPagingItems<Task>,
|
taskList: LazyPagingItems<Task>,
|
||||||
onClick: (uid: Int) -> Unit,
|
|
||||||
onDeleteClick: (task: Task) -> Unit,
|
onDeleteClick: (task: Task) -> Unit,
|
||||||
onAddToFavoritesClick: (task: Task) -> Unit,
|
onAddToFavoritesClick: (task: Task) -> Unit,
|
||||||
onEditClick: (task: Task) -> Unit
|
onEditClick: (task: Task) -> Unit
|
||||||
@ -240,7 +321,6 @@ private fun TaskList(
|
|||||||
task?.let {
|
task?.let {
|
||||||
SwipeToDelete(
|
SwipeToDelete(
|
||||||
task = task,
|
task = task,
|
||||||
onClick = onClick,
|
|
||||||
onDeleteClick = onDeleteClick,
|
onDeleteClick = onDeleteClick,
|
||||||
onAddToFavoritesClick = onAddToFavoritesClick,
|
onAddToFavoritesClick = onAddToFavoritesClick,
|
||||||
onEditClick = onEditClick
|
onEditClick = onEditClick
|
||||||
@ -257,23 +337,3 @@ private fun TaskList(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun TaskListItem(
|
|
||||||
task: Task, modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
Card(
|
|
||||||
modifier = modifier.fillMaxWidth(),
|
|
||||||
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = modifier.padding(all = 10.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = String.format("%s %s", task.name, task.description)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,17 +3,25 @@ package ru.ulstu.`is`.pmu.ui.task.list
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.paging.PagingData
|
import androidx.paging.PagingData
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import ru.ulstu.`is`.pmu.common.MyViewModel
|
||||||
import ru.ulstu.`is`.pmu.common.TaskRepository
|
import ru.ulstu.`is`.pmu.common.TaskRepository
|
||||||
import ru.ulstu.`is`.pmu.database.task.model.Task
|
import ru.ulstu.`is`.pmu.database.task.model.Task
|
||||||
|
|
||||||
class TaskListViewModel(
|
class TaskListViewModel(
|
||||||
private val taskRepository: TaskRepository
|
private val taskRepository: TaskRepository
|
||||||
) : ViewModel() {
|
) : MyViewModel() {
|
||||||
|
|
||||||
val taskListUiState: Flow<PagingData<Task>> = taskRepository.getAllTasks()
|
val taskListUiState: Flow<PagingData<Task>> = taskRepository.getAllDateTasks()
|
||||||
|
|
||||||
suspend fun deleteTask(task: Task) {
|
fun deleteTask(task: Task) {
|
||||||
taskRepository.deleteTask(task)
|
runInScope ( actionSuccess = {taskRepository.deleteTask(task)})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun favoriteTask(task: Task) {
|
||||||
|
runInScope ( actionSuccess = {taskRepository.favoriteTask(task)})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deletefavoriteTask(task: Task) {
|
||||||
|
runInScope ( actionSuccess = {taskRepository.deletefavoriteTask(task)})
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,16 +1,22 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">pmu-demo</string>
|
<string name="app_name">pmu-demo</string>
|
||||||
<string name="task_firstname">Имя</string>
|
<string name="task_firstname">Название задачи</string>
|
||||||
<string name="task_lastname">Фамилия</string>
|
<string name="task_lastname">Описание задачи</string>
|
||||||
<string name="task_user">Группа</string>
|
<string name="task_user">Пользователь</string>
|
||||||
<string name="task_phone">Телефон</string>
|
<string name="task_phone">Дата окончания</string>
|
||||||
<string name="task_email">e-mail</string>
|
<string name="task_email">e-mail</string>
|
||||||
<string name="task_main_title">Список студентов</string>
|
<string name="task_main_title">Список задач</string>
|
||||||
<string name="task_view_title">Профиль студента</string>
|
<string name="task_view_title">Профиль студента</string>
|
||||||
|
<string name="task_favorite_view_title">Избранные</string>
|
||||||
<string name="task_empty_description">Записи о студентах отсутствуют</string>
|
<string name="task_empty_description">Записи о студентах отсутствуют</string>
|
||||||
<string name="task_user_not_select">Группа не указана</string>
|
<string name="task_user_not_select">Группа не указана</string>
|
||||||
<string name="task_save_button">Сохранить</string>
|
<string name="task_save_button">Сохранить</string>
|
||||||
<string name="about_title">О нас</string>
|
<string name="about_title">О нас</string>
|
||||||
|
<string name="back">Назад</string>
|
||||||
|
<string name="loading">Загрузка…</string>
|
||||||
|
<string name="report">Отчет</string>
|
||||||
|
<string name="startDate">Дата начала</string>
|
||||||
|
<string name="endDate">Дата конца</string>
|
||||||
<string name="about_text">
|
<string name="about_text">
|
||||||
<p>Это текст <b>о нас</b>!</p>\n\n
|
<p>Это текст <b>о нас</b>!</p>\n\n
|
||||||
<p>Здесь могла быть Ваша реклама!</p>\n\n
|
<p>Здесь могла быть Ваша реклама!</p>\n\n
|
||||||
|
42
server/.gitignore
vendored
42
server/.gitignore
vendored
@ -1,42 +0,0 @@
|
|||||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
|
||||||
|
|
||||||
# Compiled output
|
|
||||||
/dist
|
|
||||||
/tmp
|
|
||||||
/out-tsc
|
|
||||||
/bazel-out
|
|
||||||
|
|
||||||
# Node
|
|
||||||
/node_modules
|
|
||||||
npm-debug.log
|
|
||||||
yarn-error.log
|
|
||||||
|
|
||||||
# IDEs and editors
|
|
||||||
.idea/
|
|
||||||
.project
|
|
||||||
.classpath
|
|
||||||
.c9/
|
|
||||||
*.launch
|
|
||||||
.settings/
|
|
||||||
*.sublime-workspace
|
|
||||||
|
|
||||||
# Visual Studio Code
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
.history/*
|
|
||||||
|
|
||||||
# Miscellaneous
|
|
||||||
/.angular/cache
|
|
||||||
.sass-cache/
|
|
||||||
/connect.lock
|
|
||||||
/coverage
|
|
||||||
/libpeerconnection.log
|
|
||||||
testem.log
|
|
||||||
/typings
|
|
||||||
|
|
||||||
# System files
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
@ -14,11 +14,15 @@
|
|||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "123456",
|
"name": "dsads",
|
||||||
"description": "32155",
|
"description": "12312312",
|
||||||
"endDate": "55",
|
"endDate": "02.11.2023"
|
||||||
"favorite": false,
|
},
|
||||||
"userId": 1
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "test",
|
||||||
|
"description": "test",
|
||||||
|
"endDate": "05.12.2023"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
629
server/package-lock.json
generated
629
server/package-lock.json
generated
@ -7,10 +7,26 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "fake-db",
|
"name": "fake-db",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"pdfkit": "^0.14.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"json-server": "0.17.4"
|
"json-server": "0.17.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/helpers": {
|
||||||
|
"version": "0.3.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.3.17.tgz",
|
||||||
|
"integrity": "sha512-tb7Iu+oZ+zWJZ3HJqwx8oNwSDIU440hmVMDPhpACWQWnrZHK99Bxs70gT1L2dnr5Hg50ZRWEFkQCAnOVVV0z1Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/helpers/node_modules/tslib": {
|
||||||
|
"version": "2.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
|
||||||
|
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
|
||||||
|
},
|
||||||
"node_modules/accepts": {
|
"node_modules/accepts": {
|
||||||
"version": "1.3.8",
|
"version": "1.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||||
@ -48,12 +64,54 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/array-buffer-byte-length": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"is-array-buffer": "^3.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/array-flatten": {
|
"node_modules/array-flatten": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/available-typed-arrays": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/base64-js": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "patreon",
|
||||||
|
"url": "https://www.patreon.com/feross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "consulting",
|
||||||
|
"url": "https://feross.org/support"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"node_modules/basic-auth": {
|
"node_modules/basic-auth": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
|
||||||
@ -90,6 +148,14 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/brotli": {
|
||||||
|
"version": "1.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
|
||||||
|
"integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@ -103,7 +169,6 @@
|
|||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
|
||||||
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
|
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2",
|
"function-bind": "^1.1.2",
|
||||||
"get-intrinsic": "^1.2.1",
|
"get-intrinsic": "^1.2.1",
|
||||||
@ -143,6 +208,14 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clone": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@ -278,6 +351,11 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/crypto-js": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q=="
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
@ -287,11 +365,46 @@
|
|||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/deep-equal": {
|
||||||
|
"version": "2.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
|
||||||
|
"integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==",
|
||||||
|
"dependencies": {
|
||||||
|
"array-buffer-byte-length": "^1.0.0",
|
||||||
|
"call-bind": "^1.0.5",
|
||||||
|
"es-get-iterator": "^1.1.3",
|
||||||
|
"get-intrinsic": "^1.2.2",
|
||||||
|
"is-arguments": "^1.1.1",
|
||||||
|
"is-array-buffer": "^3.0.2",
|
||||||
|
"is-date-object": "^1.0.5",
|
||||||
|
"is-regex": "^1.1.4",
|
||||||
|
"is-shared-array-buffer": "^1.0.2",
|
||||||
|
"isarray": "^2.0.5",
|
||||||
|
"object-is": "^1.1.5",
|
||||||
|
"object-keys": "^1.1.1",
|
||||||
|
"object.assign": "^4.1.4",
|
||||||
|
"regexp.prototype.flags": "^1.5.1",
|
||||||
|
"side-channel": "^1.0.4",
|
||||||
|
"which-boxed-primitive": "^1.0.2",
|
||||||
|
"which-collection": "^1.0.1",
|
||||||
|
"which-typed-array": "^1.1.13"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/deep-equal/node_modules/isarray": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
|
||||||
|
},
|
||||||
"node_modules/define-data-property": {
|
"node_modules/define-data-property": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
|
||||||
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
|
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.2.1",
|
"get-intrinsic": "^1.2.1",
|
||||||
"gopd": "^1.0.1",
|
"gopd": "^1.0.1",
|
||||||
@ -301,6 +414,22 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/define-properties": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
|
||||||
|
"dependencies": {
|
||||||
|
"define-data-property": "^1.0.1",
|
||||||
|
"has-property-descriptors": "^1.0.0",
|
||||||
|
"object-keys": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
@ -320,6 +449,11 @@
|
|||||||
"npm": "1.2.8000 || >= 1.4.16"
|
"npm": "1.2.8000 || >= 1.4.16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dfa": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q=="
|
||||||
|
},
|
||||||
"node_modules/ee-first": {
|
"node_modules/ee-first": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||||
@ -354,6 +488,30 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es-get-iterator": {
|
||||||
|
"version": "1.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
|
||||||
|
"integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"get-intrinsic": "^1.1.3",
|
||||||
|
"has-symbols": "^1.0.3",
|
||||||
|
"is-arguments": "^1.1.1",
|
||||||
|
"is-map": "^2.0.2",
|
||||||
|
"is-set": "^2.0.2",
|
||||||
|
"is-string": "^1.0.7",
|
||||||
|
"isarray": "^2.0.5",
|
||||||
|
"stop-iteration-iterator": "^1.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es-get-iterator/node_modules/isarray": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
|
||||||
|
},
|
||||||
"node_modules/escalade": {
|
"node_modules/escalade": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||||
@ -516,6 +674,30 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fontkit": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fontkit/-/fontkit-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-HkW/8Lrk8jl18kzQHvAw9aTHe1cqsyx5sDnxncx652+CIfhawokEPkeM3BoIC+z/Xv7a0yMr0f3pRRwhGH455g==",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/helpers": "^0.3.13",
|
||||||
|
"brotli": "^1.3.2",
|
||||||
|
"clone": "^2.1.2",
|
||||||
|
"deep-equal": "^2.0.5",
|
||||||
|
"dfa": "^1.2.0",
|
||||||
|
"restructure": "^2.0.1",
|
||||||
|
"tiny-inflate": "^1.0.3",
|
||||||
|
"unicode-properties": "^1.3.1",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/for-each": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
|
||||||
|
"integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-callable": "^1.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@ -538,7 +720,14 @@
|
|||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||||
"dev": true,
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/functions-have-names": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
@ -556,7 +745,6 @@
|
|||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
|
||||||
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
|
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2",
|
"function-bind": "^1.1.2",
|
||||||
"has-proto": "^1.0.1",
|
"has-proto": "^1.0.1",
|
||||||
@ -571,7 +759,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.1.3"
|
"get-intrinsic": "^1.1.3"
|
||||||
},
|
},
|
||||||
@ -585,6 +772,14 @@
|
|||||||
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/has-bigints": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/has-flag": {
|
"node_modules/has-flag": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
@ -598,7 +793,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
|
||||||
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
|
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"get-intrinsic": "^1.2.2"
|
"get-intrinsic": "^1.2.2"
|
||||||
},
|
},
|
||||||
@ -610,7 +804,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
@ -622,7 +815,20 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||||
"dev": true,
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/has-tostringtag": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-symbols": "^1.0.2"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
},
|
},
|
||||||
@ -634,7 +840,6 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz",
|
||||||
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
|
"integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.2"
|
"function-bind": "^1.1.2"
|
||||||
},
|
},
|
||||||
@ -676,6 +881,19 @@
|
|||||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/internal-slot": {
|
||||||
|
"version": "1.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz",
|
||||||
|
"integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==",
|
||||||
|
"dependencies": {
|
||||||
|
"get-intrinsic": "^1.2.2",
|
||||||
|
"hasown": "^2.0.0",
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ipaddr.js": {
|
"node_modules/ipaddr.js": {
|
||||||
"version": "1.9.1",
|
"version": "1.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||||
@ -685,6 +903,85 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-arguments": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-array-buffer": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"get-intrinsic": "^1.2.0",
|
||||||
|
"is-typed-array": "^1.1.10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-bigint": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-bigints": "^1.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-boolean-object": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-callable": {
|
||||||
|
"version": "1.2.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||||
|
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-date-object": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-fullwidth-code-point": {
|
"node_modules/is-fullwidth-code-point": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
@ -694,12 +991,130 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-map": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-number-object": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-promise": {
|
"node_modules/is-promise": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
|
||||||
"integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
|
"integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/is-regex": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-set": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-shared-array-buffer": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-string": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-symbol": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
|
||||||
|
"dependencies": {
|
||||||
|
"has-symbols": "^1.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-typed-array": {
|
||||||
|
"version": "1.1.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz",
|
||||||
|
"integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==",
|
||||||
|
"dependencies": {
|
||||||
|
"which-typed-array": "^1.1.11"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-weakmap": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/is-weakset": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"get-intrinsic": "^1.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/isarray": {
|
"node_modules/isarray": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||||
@ -754,6 +1169,23 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/linebreak": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "0.0.8",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/linebreak/node_modules/base64-js": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
|
||||||
|
"integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/lodash": {
|
"node_modules/lodash": {
|
||||||
"version": "4.17.21",
|
"version": "4.17.21",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
@ -940,7 +1372,46 @@
|
|||||||
"version": "1.13.1",
|
"version": "1.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||||
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
|
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
|
||||||
"dev": true,
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-is": {
|
||||||
|
"version": "1.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
|
||||||
|
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"define-properties": "^1.1.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object-keys": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/object.assign": {
|
||||||
|
"version": "4.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz",
|
||||||
|
"integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.5",
|
||||||
|
"define-properties": "^1.2.1",
|
||||||
|
"has-symbols": "^1.0.3",
|
||||||
|
"object-keys": "^1.1.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
@ -981,6 +1452,17 @@
|
|||||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
|
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/pdfkit": {
|
||||||
|
"version": "0.14.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.14.0.tgz",
|
||||||
|
"integrity": "sha512-Hnor8/78jhHm6ONrxWhrqOwAVALlBnFyWOF8sstBZMiqHZgZ5A6RU+Q3yahhw82plxpT7LOfH3b3qcOX6rzMQg==",
|
||||||
|
"dependencies": {
|
||||||
|
"crypto-js": "^4.2.0",
|
||||||
|
"fontkit": "^1.8.1",
|
||||||
|
"linebreak": "^1.0.2",
|
||||||
|
"png-js": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pify": {
|
"node_modules/pify": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
|
||||||
@ -1008,6 +1490,11 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/png-js": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/png-js/-/png-js-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g=="
|
||||||
|
},
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@ -1060,6 +1547,22 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/regexp.prototype.flags": {
|
||||||
|
"version": "1.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz",
|
||||||
|
"integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.2",
|
||||||
|
"define-properties": "^1.2.0",
|
||||||
|
"set-function-name": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/require-directory": {
|
"node_modules/require-directory": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
@ -1069,6 +1572,11 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/restructure": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/restructure/-/restructure-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-e0dOpjm5DseomnXx2M5lpdZ5zoHqF1+bqdMJUohoYVVQa7cBdnk7fdmeI6byNWP/kiME72EeTiSypTCVnpLiDg=="
|
||||||
|
},
|
||||||
"node_modules/safe-buffer": {
|
"node_modules/safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
@ -1142,7 +1650,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
|
||||||
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
|
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"define-data-property": "^1.1.1",
|
"define-data-property": "^1.1.1",
|
||||||
"get-intrinsic": "^1.2.1",
|
"get-intrinsic": "^1.2.1",
|
||||||
@ -1153,6 +1660,19 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-function-name": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==",
|
||||||
|
"dependencies": {
|
||||||
|
"define-data-property": "^1.0.1",
|
||||||
|
"functions-have-names": "^1.2.3",
|
||||||
|
"has-property-descriptors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/setprototypeof": {
|
"node_modules/setprototypeof": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||||
@ -1163,7 +1683,6 @@
|
|||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||||
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"call-bind": "^1.0.0",
|
"call-bind": "^1.0.0",
|
||||||
"get-intrinsic": "^1.0.2",
|
"get-intrinsic": "^1.0.2",
|
||||||
@ -1191,6 +1710,17 @@
|
|||||||
"graceful-fs": "^4.1.3"
|
"graceful-fs": "^4.1.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stop-iteration-iterator": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"internal-slot": "^1.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
@ -1229,6 +1759,11 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tiny-inflate": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="
|
||||||
|
},
|
||||||
"node_modules/toidentifier": {
|
"node_modules/toidentifier": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||||
@ -1251,6 +1786,29 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unicode-properties": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
|
||||||
|
"integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.3.0",
|
||||||
|
"unicode-trie": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unicode-trie": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"pako": "^0.2.5",
|
||||||
|
"tiny-inflate": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/unicode-trie/node_modules/pako": {
|
||||||
|
"version": "0.2.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
|
||||||
|
"integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="
|
||||||
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||||
@ -1278,6 +1836,53 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/which-boxed-primitive": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-bigint": "^1.0.1",
|
||||||
|
"is-boolean-object": "^1.1.0",
|
||||||
|
"is-number-object": "^1.0.4",
|
||||||
|
"is-string": "^1.0.5",
|
||||||
|
"is-symbol": "^1.0.3"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/which-collection": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-map": "^2.0.1",
|
||||||
|
"is-set": "^2.0.1",
|
||||||
|
"is-weakmap": "^2.0.1",
|
||||||
|
"is-weakset": "^2.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/which-typed-array": {
|
||||||
|
"version": "1.1.13",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz",
|
||||||
|
"integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==",
|
||||||
|
"dependencies": {
|
||||||
|
"available-typed-arrays": "^1.0.5",
|
||||||
|
"call-bind": "^1.0.4",
|
||||||
|
"for-each": "^0.3.3",
|
||||||
|
"gopd": "^1.0.1",
|
||||||
|
"has-tostringtag": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wrap-ansi": {
|
"node_modules/wrap-ansi": {
|
||||||
"version": "7.0.0",
|
"version": "7.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
"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 ./server.js --host 0.0.0.0 -p 8079"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"pdfkit": "^0.14.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"json-server": "0.17.4"
|
"json-server": "0.17.4"
|
||||||
|
Loading…
Reference in New Issue
Block a user