coursework complete

This commit is contained in:
AnnZhimol 2023-12-26 16:16:13 +04:00
parent 9b7acce5b6
commit 00169e7020
20 changed files with 667 additions and 124 deletions

View File

@ -12,6 +12,6 @@
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2023-10-27T17:41:35.449717700Z" />
<timeTargetWasSelectedWithDropDown value="2023-12-26T09:35:26.870656500Z" />
</component>
</project>

View File

@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="corretto-17" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="corretto-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -6,10 +6,10 @@ import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.rememberNavController
import com.example.pmulabs.viewModels.CurrentUserViewModel
import com.example.pmulabs.graphs.RootNavigationGraph
import com.example.pmulabs.ui.theme.PMULabsTheme
import com.example.pmulabs.viewModels.AppViewModelProvider
import com.example.pmulabs.viewModels.CurrentUserViewModel
import com.example.pmulabs.viewModels.SearchViewModel
class MainActivity : ComponentActivity() {
@ -19,7 +19,7 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
setContent {
PMULabsTheme {
RootNavigationGraph(navController = rememberNavController(),searchViewModel, currentUserViewModel = viewModel(factory = AppViewModelProvider.Factory))
RootNavigationGraph(navController = rememberNavController(),searchViewModel=searchViewModel, currentUserViewModel = viewModel(factory = AppViewModelProvider.Factory))
}
}
}

View File

@ -2,6 +2,7 @@ package com.example.pmulabs.api
import com.example.pmulabs.api.model.ArticleRemote
import com.example.pmulabs.api.model.CommentRemote
import com.example.pmulabs.api.model.ReportRemote
import com.example.pmulabs.api.model.TagRemote
import com.example.pmulabs.api.model.UserRemote
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
@ -19,6 +20,11 @@ import retrofit2.http.Path
import retrofit2.http.Query
interface NewsPortalService {
@GET("report")
suspend fun getReport(
@Query("startDate") startDate: Long,
@Query("endDate") endDate: Long
): List<ReportRemote>
@GET("users")
suspend fun getUsers(): List<UserRemote>
@GET("tags")

View File

@ -0,0 +1,12 @@
package com.example.pmulabs.api.model
import kotlinx.serialization.Serializable
@Serializable
data class ReportRemote(
val title: String = "",
val publishDate: Long = 0,
val tagName: String = "",
val userName: String = "",
val comments: Int = 0
)

View File

@ -1,6 +1,7 @@
package com.example.pmulabs.api.repository
import com.example.pmulabs.api.NewsPortalService
import com.example.pmulabs.api.model.ReportRemote
import com.example.pmulabs.api.model.toArticle
import com.example.pmulabs.api.model.toComment
import com.example.pmulabs.api.model.toTag
@ -27,6 +28,11 @@ class RestUserRepository(
service.createUser(user.toUserRemote()).toUser()
}
override suspend fun getReport(startDate: Long, endDate: Long):List<ReportRemote>
{
return service.getReport(startDate,endDate)
}
override suspend fun updateUser(user: User) {
user.id?.let { service.updateUser(it, user.toUserRemote()).toUser() }
}

View File

@ -84,4 +84,10 @@ sealed class BottomBarScreen(
icon= R.drawable.ic_bottom_info,
iconFocused=R.drawable.ic_bottom_info_focused
)
object Report: BottomBarScreen(
route = "REPORT",
title="Report",
icon= R.drawable.ic_bottom_info,
iconFocused=R.drawable.ic_bottom_info_focused
)
}

View File

@ -18,6 +18,7 @@ import com.example.pmulabs.screensMobile.CoopScreen
import com.example.pmulabs.screensMobile.InfoScreen
import com.example.pmulabs.screensMobile.MainScreen
import com.example.pmulabs.screensMobile.ProfileScreen
import com.example.pmulabs.screensMobile.ReportScreen
import com.example.pmulabs.screensMobile.TagsScreen
import com.example.pmulabs.screensMobile.filterScreens.CalendarScreen
import com.example.pmulabs.screensMobile.filterScreens.SearchByTagScreen
@ -26,10 +27,11 @@ import com.example.pmulabs.viewModels.AppViewModelProvider
import com.example.pmulabs.viewModels.ArticlePageScreenViewModel
import com.example.pmulabs.viewModels.ArticleScreenViewModel
import com.example.pmulabs.viewModels.CurrentUserViewModel
import com.example.pmulabs.viewModels.ReportViewModel
import com.example.pmulabs.viewModels.TagItemViewModel
@Composable
fun HomeNavGraph(navController: NavHostController,articlePageScreenViewModel: ArticlePageScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), tagItemViewModel: TagItemViewModel = viewModel(factory = AppViewModelProvider.Factory), articleScreenViewModel: ArticleScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
fun HomeNavGraph(navController: NavHostController,reportViewModel: ReportViewModel= viewModel(factory = AppViewModelProvider.Factory),articlePageScreenViewModel: ArticlePageScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), tagItemViewModel: TagItemViewModel = viewModel(factory = AppViewModelProvider.Factory), articleScreenViewModel: ArticleScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
NavHost(
navController = navController,
route = Graph.MAIN,
@ -84,5 +86,8 @@ fun HomeNavGraph(navController: NavHostController,articlePageScreenViewModel: Ar
composable(route=BottomBarScreen.Categories.route){
TagsScreen(navController,Modifier, tagItemViewModel, currentUserViewModel)
}
composable(route=BottomBarScreen.Report.route){
ReportScreen(navController, reportViewModel)
}
}
}

View File

@ -14,13 +14,14 @@ import com.example.pmulabs.viewModels.ArticleScreenViewModel
import com.example.pmulabs.viewModels.CurrentUserViewModel
import com.example.pmulabs.viewModels.EntryScreenViewModel
import com.example.pmulabs.viewModels.RegisterScreenViewModel
import com.example.pmulabs.viewModels.ReportViewModel
import com.example.pmulabs.viewModels.SearchViewModel
import com.example.pmulabs.viewModels.TagItemViewModel
const val USERID_ARGUMENT="userId"
@Composable
fun RootNavigationGraph(navController: NavHostController, searchViewModel: SearchViewModel,articlePageScreenViewModel: ArticlePageScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), tagItemViewModel: TagItemViewModel = viewModel(factory = AppViewModelProvider.Factory), articleScreenViewModel: ArticleScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), registerScreenViewModel: RegisterScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), entryScreenViewModel: EntryScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
fun RootNavigationGraph(navController: NavHostController,reportViewModel: ReportViewModel= viewModel(factory = AppViewModelProvider.Factory), searchViewModel: SearchViewModel,articlePageScreenViewModel: ArticlePageScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), tagItemViewModel: TagItemViewModel = viewModel(factory = AppViewModelProvider.Factory), articleScreenViewModel: ArticleScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), registerScreenViewModel: RegisterScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), entryScreenViewModel: EntryScreenViewModel = viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)){
NavHost(
navController=navController,
route = Graph.ROOT,
@ -31,7 +32,7 @@ fun RootNavigationGraph(navController: NavHostController, searchViewModel: Searc
arguments = listOf(navArgument(USERID_ARGUMENT){
type= NavType.StringType
})){
LoadScreen(searchViewModel = searchViewModel, articleScreenViewModel = articleScreenViewModel, articlePageScreenViewModel = articlePageScreenViewModel, tagItemViewModel = tagItemViewModel,currentUserViewModel = currentUserViewModel)
LoadScreen(searchViewModel = searchViewModel,reportViewModel=reportViewModel, articleScreenViewModel = articleScreenViewModel, articlePageScreenViewModel = articlePageScreenViewModel, tagItemViewModel = tagItemViewModel,currentUserViewModel = currentUserViewModel)
}
}
}

View File

@ -1,5 +1,6 @@
package com.example.pmulabs.room.repository
import com.example.pmulabs.api.model.ReportRemote
import com.example.pmulabs.room.dao.UserDao
import com.example.pmulabs.room.models.Article
import com.example.pmulabs.room.models.Comment
@ -16,4 +17,8 @@ class OfflineUserRepository(private val userDao: UserDao) : UserRepository {
override suspend fun getUserComms(idUser: Int): List<Comment> = userDao.getUserComms(idUser)
override suspend fun getUserArticles(idUser: Int): List<Article> = userDao.getUserArticles(idUser)
override suspend fun getUserTags(idUser: Int): List<Tag> = userDao.getUserTags(idUser)
override suspend fun getReport(startDate: Long, endDate: Long): List<ReportRemote> {
TODO("Not yet implemented")
}
}

View File

@ -1,5 +1,6 @@
package com.example.pmulabs.room.repository
import com.example.pmulabs.api.model.ReportRemote
import com.example.pmulabs.room.models.Article
import com.example.pmulabs.room.models.Comment
import com.example.pmulabs.room.models.Tag
@ -14,4 +15,5 @@ interface UserRepository {
suspend fun getUserComms(idUser: Int): List<Comment>
suspend fun getUserArticles(idUser: Int): List<Article>
suspend fun getUserTags(idUser: Int): List<Tag>
suspend fun getReport(startDate: Long, endDate: Long):List<ReportRemote>
}

View File

@ -46,6 +46,7 @@ import com.example.pmulabs.viewModels.AppViewModelProvider
import com.example.pmulabs.viewModels.ArticlePageScreenViewModel
import com.example.pmulabs.viewModels.ArticleScreenViewModel
import com.example.pmulabs.viewModels.CurrentUserViewModel
import com.example.pmulabs.viewModels.ReportViewModel
import com.example.pmulabs.viewModels.SearchViewModel
import com.example.pmulabs.viewModels.SearchWidget
import com.example.pmulabs.viewModels.TagItemViewModel
@ -94,7 +95,7 @@ fun MainScreen(navController: NavController, modifier: Modifier = Modifier, arti
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun LoadScreen(navController: NavHostController=rememberNavController(),articlePageScreenViewModel: ArticlePageScreenViewModel, tagItemViewModel: TagItemViewModel, articleScreenViewModel: ArticleScreenViewModel, searchViewModel: SearchViewModel, currentUserViewModel: CurrentUserViewModel){
fun LoadScreen(navController: NavHostController=rememberNavController(), reportViewModel: ReportViewModel= viewModel(factory = AppViewModelProvider.Factory),articlePageScreenViewModel: ArticlePageScreenViewModel, tagItemViewModel: TagItemViewModel, articleScreenViewModel: ArticleScreenViewModel, searchViewModel: SearchViewModel, currentUserViewModel: CurrentUserViewModel){
val searchWidgetState by searchViewModel.searchWidgetState
val searchTextState by searchViewModel.searchTextState
@ -257,7 +258,7 @@ fun LoadScreen(navController: NavHostController=rememberNavController(),articleP
) {
Modifier
.padding(it)
HomeNavGraph(navController = navController,articlePageScreenViewModel, tagItemViewModel,articleScreenViewModel,currentUserViewModel)
HomeNavGraph(navController = navController, reportViewModel = reportViewModel,articlePageScreenViewModel, tagItemViewModel,articleScreenViewModel,currentUserViewModel)
}
}

View File

@ -1,17 +1,14 @@
package com.example.pmulabs.screensMobile
import android.annotation.SuppressLint
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
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.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
@ -33,7 +30,6 @@ import androidx.compose.material3.ButtonColors
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
@ -62,6 +58,7 @@ import androidx.compose.ui.unit.toSize
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.pmulabs.R
import com.example.pmulabs.basecomponents.navigate.BottomBarScreen
import com.example.pmulabs.designElem.elem.ValidateEmail
import com.example.pmulabs.designElem.elem.isValidEmail
import com.example.pmulabs.room.models.Article
@ -79,13 +76,11 @@ import java.util.Date
@Composable
fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,articleScreenViewModel: ArticleScreenViewModel= viewModel(factory = AppViewModelProvider.Factory),tagItemViewModel: TagItemViewModel= viewModel(factory = AppViewModelProvider.Factory),articlePageScreenViewModel: ArticlePageScreenViewModel= viewModel(factory = AppViewModelProvider.Factory), currentUserViewModel: CurrentUserViewModel = viewModel(factory = AppViewModelProvider.Factory)) {
articleScreenViewModel.setArticleList()
var getUser by remember { mutableStateOf(currentUserViewModel.user) }
var openDialogUser by remember { mutableStateOf(false) }
var email by remember { mutableStateOf(getUser?.email) }
var password by remember { mutableStateOf(getUser?.password) }
var nickname by remember { mutableStateOf(getUser?.nickname) }
val getArticles = articleScreenViewModel.getArticles
val coroutineScope = rememberCoroutineScope()
articlePageScreenViewModel.setTagList()
@ -515,129 +510,36 @@ fun ProfileScreen(navController: NavController, modifier: Modifier = Modifier,ar
.wrapContentHeight(align = Alignment.CenterVertically)
.clickable { openDialogUser = true })
}
}
}
item {
Spacer(modifier = Modifier.padding(10.dp))
Box(
modifier = Modifier
.offset(
x = 9.dp,
y = 220.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 282.dp)
) {
Box(
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 0.dp,
y = 27.1240234375.dp
y = 419.dp
)
.requiredWidth(width = 393.dp)
.requiredHeight(height = 255.dp)
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 255.dp)
.clip(shape = RoundedCornerShape(5.dp))
.background(color = Color.White)
.border(
border = BorderStroke(3.dp, Color(0xffdbdbf1)),
shape = RoundedCornerShape(5.dp)
)
)
LazyColumn(
contentPadding = PaddingValues(
top = 28.dp,
bottom = 0.dp,
start = 10.dp,
end = 10.dp
),
verticalArrangement = Arrangement.spacedBy(1.dp)
) {
if (getArticles.size != 0) {
items(count = getArticles.size) { index ->
val article = getArticles[index]
if (article?.userId == getUser?.id) {
Spacer(modifier = Modifier.padding(5.dp))
Text(
text = "${article?.title}",
color = Color(0xff423a99),
style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.align(alignment = Alignment.TopStart)
/*.clickable {
navController.navigate(
BottomBarScreen.ArticlePage.passId(
article?.id.toString()
)
)
}*/
)
Spacer(modifier = Modifier.padding(5.dp))
HorizontalDivider(
thickness = 3.dp,
modifier = Modifier
.border(BorderStroke(3.dp, Color(0xffdbdbf1))),
color = Color(0xffdbdbf1)
)
}
}
} else {
item {
Text(
text = "Статей пока нет!",
color = Color(0xff423a99),
style = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.align(alignment = Alignment.TopStart)
)
}
}
}
}
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 54.dp)
) {
Box(
modifier = Modifier
.requiredWidth(width = 393.dp)
.requiredHeight(height = 54.dp)
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
.clip(shape = RoundedCornerShape(15.dp))
.background(color = Color(0xff423a99))
)
Text(
text = "Articles:",
color = Color.White,
text = "Get Report",
color = Color(0xffdbdbf1),
textAlign = TextAlign.Center,
style = TextStyle(
fontSize = 17.sp,
fontWeight = FontWeight.Bold
),
modifier = Modifier
.align(alignment = Alignment.TopStart)
.offset(
x = 24.dp,
y = 0.dp
)
.requiredWidth(width = 210.dp)
.requiredHeight(height = 54.dp)
.requiredWidth(width = 200.dp)
.requiredHeight(height = 44.dp)
.wrapContentHeight(align = Alignment.CenterVertically)
)
.clickable { navController.navigate(BottomBarScreen.Report.route)})
}
}
}

View File

@ -0,0 +1,280 @@
package com.example.pmulabs.screensMobile
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.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonColors
import androidx.compose.material3.DatePicker
import androidx.compose.material3.DatePickerColors
import androidx.compose.material3.DisplayMode
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import com.example.pmulabs.api.model.ReportRemote
import com.example.pmulabs.designElem.elem.BackButton
import com.example.pmulabs.viewModels.AppViewModelProvider
import com.example.pmulabs.viewModels.ReportViewModel
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Date
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ReportScreen(navController: NavController?, viewModel: ReportViewModel = viewModel(factory = AppViewModelProvider.Factory))
{
val dateStateStart = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
val dateStateEnd = rememberDatePickerState(initialDisplayMode = DisplayMode.Input)
val coroutineScope = rememberCoroutineScope()
val reportResultPageState = viewModel.reportResultPageUiState
LazyColumn(
contentPadding = PaddingValues(top=75.dp, bottom = 70.dp, start = 10.dp,end=10.dp),
verticalArrangement = Arrangement.spacedBy(15.dp),
modifier = Modifier
.background(Color.White)
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
)
{
item{
BackButton(modifier=Modifier
.clickable {
reportResultPageState.resReport= emptyList()
navController?.popBackStack()
})
}
item {
DatePicker(
title = {
Text(
text = "Start Date",
style = MaterialTheme.typography.headlineLarge
)
},
state = dateStateStart,
colors= DatePickerColors(
containerColor = Color.White,
currentYearContentColor=Color(0xff423a99),
dateTextFieldColors = TextFieldDefaults.textFieldColors(
Color(0xff423a99),
containerColor = Color.White,
focusedLabelColor = Color(0xff423a99),
focusedPlaceholderColor = Color(0xff423a99),
focusedTrailingIconColor = Color(0xff423a99)
),
dayContentColor=Color(0xff423a99),
dayInSelectionRangeContainerColor=Color(0xff423a99),
dayInSelectionRangeContentColor=Color(0xff423a99),
disabledDayContentColor=Color(0xff423a99),
disabledSelectedDayContainerColor=Color(0xff423a99),
disabledSelectedDayContentColor=Color(0xff423a99),
disabledSelectedYearContainerColor=Color(0xff423a99),
disabledSelectedYearContentColor=Color(0xff423a99),
disabledYearContentColor=Color(0xff423a99),
dividerColor=Color(0xff423a99),
headlineContentColor=Color(0xff423a99),
navigationContentColor=Color(0xff423a99),
selectedDayContainerColor=Color(0xff423a99),
selectedDayContentColor=Color.White,
selectedYearContainerColor=Color(0xff423a99),
selectedYearContentColor=Color.White,
subheadContentColor=Color(0xff423a99),
titleContentColor=Color(0xff423a99),
todayContentColor=Color(0xff423a99),
todayDateBorderColor=Color(0xff423a99),
weekdayContentColor=Color(0xff423a99),
yearContentColor=Color(0xff423a99)
),
)
val selectedDateStart = dateStateStart.selectedDateMillis
if (selectedDateStart != null) {
val resultDate = selectedDateStart
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(startDate = resultDate))
} else {
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(startDate = 0))
}
DatePicker(
title ={
Text(
text = "End Date",
style = MaterialTheme.typography.headlineLarge
)},
state = dateStateEnd,
colors= DatePickerColors(
containerColor = Color.White,
currentYearContentColor=Color(0xff423a99),
dateTextFieldColors = TextFieldDefaults.textFieldColors(
Color(0xff423a99),
containerColor = Color.White,
focusedLabelColor = Color(0xff423a99),
focusedPlaceholderColor = Color(0xff423a99),
focusedTrailingIconColor = Color(0xff423a99)
),
dayContentColor=Color(0xff423a99),
dayInSelectionRangeContainerColor=Color(0xff423a99),
dayInSelectionRangeContentColor=Color(0xff423a99),
disabledDayContentColor=Color(0xff423a99),
disabledSelectedDayContainerColor=Color(0xff423a99),
disabledSelectedDayContentColor=Color(0xff423a99),
disabledSelectedYearContainerColor=Color(0xff423a99),
disabledSelectedYearContentColor=Color(0xff423a99),
disabledYearContentColor=Color(0xff423a99),
dividerColor=Color(0xff423a99),
headlineContentColor=Color(0xff423a99),
navigationContentColor=Color(0xff423a99),
selectedDayContainerColor=Color(0xff423a99),
selectedDayContentColor=Color.White,
selectedYearContainerColor=Color(0xff423a99),
selectedYearContentColor=Color.White,
subheadContentColor=Color(0xff423a99),
titleContentColor=Color(0xff423a99),
todayContentColor=Color(0xff423a99),
todayDateBorderColor=Color(0xff423a99),
weekdayContentColor=Color(0xff423a99),
yearContentColor=Color(0xff423a99)
),
)
val selectedDateEnd = dateStateEnd.selectedDateMillis
if (selectedDateEnd != null) {
val resultDate = selectedDateEnd
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(endDate = resultDate))
} else {
viewModel.onUpdate(viewModel.reportPageUiState.reportDetails.copy(endDate = 0))
}
Spacer(modifier = Modifier.height(16.dp))
Button(
colors = ButtonColors(
containerColor = Color(0xff423a99),
disabledContainerColor = Color(0xff423a99),
contentColor = Color.White,
disabledContentColor = Color.White
),
onClick = {
coroutineScope.launch { viewModel.getReport() }
},
enabled = viewModel.reportPageUiState.isEntryValid,
modifier = Modifier
.fillMaxWidth(0.5f)
.height(50.dp)
) {
Text(text = "Get Report", fontSize = 20.sp)
}
Spacer(modifier = Modifier.height(32.dp))
Text(
color = Color(0xff423a99),
text = "Result",
style = MaterialTheme.typography.headlineLarge
)
TableScreen(reportData = reportResultPageState.resReport)
}
}
}
@Composable
fun ItemCell(
title: String,
commentCount: Int,
category: String,
userName: String,
date: String
) {
Box(
modifier = Modifier
.width(400.dp)
.padding(8.dp)
.clip(shape = RoundedCornerShape(5.dp))
.background(Color(0xffdbdbf1))
) {
Column(Modifier.padding(16.dp)) {
Text(
text = "Заголовок: $title",
color = Color(0xff423a99),
style = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold),
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = "Кол-во комментариев: $commentCount",
color = Color(0xff423a99),
style = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold),
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = "Категория: $category",
color = Color(0xff423a99),
style = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold),
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = "Автор: $userName",
color = Color(0xff423a99),
style = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold),
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = "Дата публикации: $date",
color = Color(0xff423a99),
style = TextStyle(fontSize = 14.sp, fontWeight = FontWeight.Bold),
modifier = Modifier.padding(bottom = 8.dp)
)
}
}
}
@Composable
fun TableScreen(reportData: List<ReportRemote>) {
val column1Weight = .8f // 30%
val column2Weight = 1f // 30%
val formatter = SimpleDateFormat("dd-MMMM-YY")
Column(
Modifier
.padding(16.dp)) {
// Here are all the lines of your table.
reportData.forEach {
val (title, publishDate, tagName, userName,comments) = it
Row(Modifier.fillMaxWidth()) {
ItemCell(
title,
comments,
tagName,
userName,
formatter.format(Date(publishDate)).toString()
)
}
}
}
}

View File

@ -32,6 +32,9 @@ object AppViewModelProvider {
initializer {
ArticlePageScreenViewModel(newsPortalApplication().container.tagRestRepository,newsPortalApplication().container.commentRestRepository,newsPortalApplication().container.articleRestRepository,newsPortalApplication().container.userRestRepository)
}
initializer {
ReportViewModel(newsPortalApplication().container.userRestRepository)
}
}
}

View File

@ -44,6 +44,7 @@ class ArticlePageScreenViewModel(
}
}
var userList by mutableStateOf<List<User>>(emptyList())
fun setUserList() {
viewModelScope.launch(Dispatchers.IO) {

View File

@ -0,0 +1,47 @@
package com.example.pmulabs.viewModels
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.example.pmulabs.api.model.ReportRemote
import com.example.pmulabs.room.repository.UserRepository
class ReportViewModel(
private val userRepository: UserRepository
) : ViewModel() {
var reportPageUiState by mutableStateOf(ReportPageUiState())
private set
var reportResultPageUiState by mutableStateOf(ReportResultPageUiState())
private set
fun onUpdate(reportDetails: ReportDetails)
{
reportPageUiState = ReportPageUiState(reportDetails = reportDetails,isEntryValid = validateInput(reportDetails))
}
private fun validateInput(uiState: ReportDetails = reportPageUiState.reportDetails): Boolean {
return with(uiState) {
startDate >=0
&& endDate >=0
&& startDate < endDate
}
}
suspend fun getReport(){
val res = userRepository.getReport(reportPageUiState.reportDetails.startDate, reportPageUiState.reportDetails.endDate)
reportResultPageUiState = ReportResultPageUiState(res)
}
}
data class ReportDetails(
val startDate: Long = 0,
val endDate: Long = 0
)
data class ReportPageUiState(
val reportDetails: ReportDetails = ReportDetails(),
val isEntryValid: Boolean = false
)
data class ReportResultPageUiState(
var resReport:List<ReportRemote> = emptyList()
)

225
server/data.json Normal file
View File

@ -0,0 +1,225 @@
{
"users": [
{
"id": 1,
"nickname": "LatinosMishas",
"email": "llmisha@gmail.com",
"password": "user1",
"role": "user"
},
{
"id": 2,
"nickname": "Kasablanka",
"email": "kasyul@gmail.com",
"password": "user2",
"role": "user"
},
{
"id": 3,
"nickname": "Ilonherber",
"email": "ihlon82@gmail.com",
"password": "user3",
"role": "user"
},
{
"id": 4,
"nickname": "AnnaLibert",
"email": "libert@gmail.com",
"password": "user4",
"role": "user"
},
{
"id": 5,
"nickname": "ffff",
"email": "fff@mail.ru",
"password": "qqq",
"role": "user"
}
],
"tags": [
{
"id": 1,
"title": "Тег_1",
"userId": 2
},
{
"title": "Тег_2",
"id": 2,
"userId": 1
},
{
"id": 3,
"title": "Тег_3",
"userId": 3
},
{
"id": 4,
"title": "Тег_4",
"userId": 2
},
{
"title": "Тег_5",
"id": 5,
"userId": 1
},
{
"id": 6,
"title": "Тег_6",
"userId": 3
}
],
"articles": [
{
"id": 1,
"title": "Tesla Cybertruck, уничтожающий всех по прямой, застрял на небольшом холме. Его вытащил Ford",
"text": "У автомобили были проблемы с ПО и летняя резина\nВ Сети появился видеоролик, в котором показано, как новейший Tesla Cybertruck, уничтожающий конкурентов в гонках по прямой, не смог подняться в горку.\n\nTesla Cybertruck с ёлкой в багажнике застрял на склоне небольшого заснеженного холма. Его при помощи буксировочной тросса пытался вытащить пикап Ford. Достать автомобиль удалось с большим трудом.",
"publishDate": 1687392000000,
"userId": 1,
"tagId": 6
},
{
"id": 2,
"title": "Заголовок 2",
"text": "Текст статьи с заголовком 2",
"publishDate": 1670889600000,
"userId": 3,
"tagId": 2
},
{
"id": 3,
"title": "Заголовок 3",
"text": "Текст статьи с заголовком 3",
"publishDate": 1685836800000,
"userId": 2,
"tagId": 1
},
{
"id": 4,
"title": "Заголовок 4",
"text": "Текст статьи с заголовком 4",
"publishDate": 1678752000000,
"userId": 3,
"tagId": 3
},
{
"id": 5,
"title": "Ракету «Союз» с новой «Арктикой» установили на старте Байконура",
"text": "Пуск запланирован на 16 декабря\nСегодня, 13 декабря 2023 года, ракету космического назначения «Союз-2.1б» с гидрометеорологическим спутником «Арктика-М» № 2 транспортировали на стартовый комплекс 31-й площадки космодрома Байконур. Об этом рассказала пресс-служба Роскосмоса. ",
"publishDate": 1674086400000,
"userId": 1,
"tagId": 4
},
{
"id": 6,
"title": "Заголовок 6",
"text": "Текст статьи с заголовком 6",
"publishDate": 1687392000000,
"userId": 2,
"tagId": 4
},
{
"id": 7,
"title": "Заголовок 7",
"text": "Текст статьи с заголовком 7",
"publishDate": 1674086400000,
"userId": 3,
"tagId": 5
},
{
"id": 8,
"title": "«Сбер» решил выпускать телевизоры диагональю до 75 дюймов в Новгородской области. Вся продукция будет проходить строгий контроль качества",
"text": "На заводе будут работать более 200 человек\n«Сбер» планирует расширить производство умных телевизоров в России, выбрав для этого Новгородскую область. Об этом сообщает инсайдерский канал «ё-Пром | Импортозамещение в промышленности».\n\nКомпания намерена локализовать до 50% производства своих телевизоров в особой экономической зоне «Новгородская» до конца 2024 года, сотрудничая при этом со своим партнером SberDevices.\n\nУмные телевизоры Sber поступили в продажу с марта 2022 года и в настоящее время производятся на заводе «Витязь» в Беларуси. Оснащение российской производственной площадки запланировано на следующий год, а запуск производства телевизоров с диагональю от 43 до 75 дюймов намечен на четвертый квартал 2024 года. Телевизоры будут работать под управлением операционной системы «Салют ТВ».",
"publishDate": 1670889600000,
"userId": 1,
"tagId": 3
},
{
"id": 9,
"title": "Epic Games после победы в суде над Google переключит внимание на Apple",
"text": "Тим Суини прямо сказал:\n\n«Апелляция Apple находится в очереди на рассмотрение в Верховном суде США».\n\nВ прошлом Epic Games уже боролась с Apple в аналогичном судебном процессе. Решение было принято в пользу Apple, но Тим Суини сейчас как никогда мотивирован, чтобы это оспорить.\n\nПомимо Apple, Тим Суини выразил заинтересованность в борьбе с такими платформами, как Steam и Xbox. По сути, компания хочет вывести Fortnite на все платформы без требования о 30%-ном отчислении владельцу платформы.\n\nИлон Макс поздравил Тима Суини с победой, подчеркнув, что она в будущем может привести к серьезным изменениям.",
"publishDate": 1702460121998,
"userId": 1,
"tagId": 4
}
],
"comments": [
{
"id": 1,
"text": "Текст комментария 1",
"userId": 1,
"articleId": 8
},
{
"id": 2,
"text": "Текст комментария 2",
"userId": 2,
"articleId": 2
},
{
"id": 3,
"text": "Текст комментария 3",
"userId": 3,
"articleId": 3
},
{
"id": 4,
"text": "Текст комментария 4",
"userId": 1,
"articleId": 4
},
{
"id": 5,
"text": "Текст комментария 5",
"userId": 2,
"articleId": 1
},
{
"id": 6,
"text": "Текст комментария 6",
"userId": 3,
"articleId": 4
},
{
"id": 7,
"text": "Текст комментария 7",
"userId": 1,
"articleId": 6
},
{
"id": 8,
"text": "Текст комментария 8",
"userId": 2,
"articleId": 7
},
{
"id": 9,
"text": "Текст комментария 9",
"userId": 3,
"articleId": 1
},
{
"id": 11,
"text": "Текст комментария 11",
"userId": 2,
"articleId": 3
},
{
"id": 12,
"text": "Текст комментария 102",
"userId": 1,
"articleId": 2
},
{
"id": 13,
"text": "новый комментарий",
"userId": 1,
"articleId": 1
},
{
"id": 14,
"text": "aaaa",
"userId": 1,
"articleId": 2
}
]
}

View File

@ -2,7 +2,7 @@
"name": "fake-db",
"version": "1.0.0",
"scripts": {
"start": "json-server --watch data.json --host 0.0.0.0 -p 8079"
"start": "json-server --watch data.json --middlewares ./report.js --host 0.0.0.0 -p 8079"
},
"dependencies": {
},

44
server/report.js Normal file
View File

@ -0,0 +1,44 @@
module.exports = (req, res, next) => {
const isReportRequest = req.url.startsWith("/report") && req.method === "GET";
if (!isReportRequest) {
next();
return;
}
try {
const { startDate, endDate } = req.query;
delete require.cache[require.resolve("./data.json")];
const data = require("./data.json");
const filteredArticles = data.articles.filter((article) => {
const articleDate = new Date(article.publishDate);
return (
articleDate >= startDate && articleDate <= endDate
);
});
console.log(filteredArticles);
const articleComm = filteredArticles.map((article) => {
const commentsCount = data.comments.filter(
(comment) => comment.articleId === article.id
).length;
const tag = data.tags.filter(
(tag) => tag.id === article.tagId
)[0].title;
const user = data.users.filter(
(user) => user.id === article.userId
)[0].nickname;
return { title: article.title, publishDate: article.publishDate,tagName:tag, userName:user, comments: commentsCount };
});
articleComm.sort((a, b) => b.publishDate - a.publishDate)
console.log(articleComm);
res.json(articleComm);
} catch (error) {
console.error("Error processing report: ", error);
res.status(500).json({ message: "Internal Server Error" });
}
};