From 00169e702056a5153ef9e18f09ded10ca0ecde4b Mon Sep 17 00:00:00 2001 From: AnnZhimol Date: Tue, 26 Dec 2023 16:16:13 +0400 Subject: [PATCH] coursework complete --- .idea/deploymentTargetDropDown.xml | 2 +- .idea/misc.xml | 7 +- .../java/com/example/pmulabs/MainActivity.kt | 4 +- .../example/pmulabs/api/NewsPortalService.kt | 6 + .../example/pmulabs/api/model/ReportRemote.kt | 12 + .../api/repository/RestUserRepository.kt | 6 + .../navigate/BottomBarScreen.kt | 6 + .../example/pmulabs/graphs/HomeNavGraph.kt | 7 +- .../example/pmulabs/graphs/RootNavGraph.kt | 5 +- .../room/repository/OfflineUserRepository.kt | 5 + .../pmulabs/room/repository/UserRepository.kt | 2 + .../pmulabs/screensMobile/MainScreen.kt | 5 +- .../pmulabs/screensMobile/ProfileScreen.kt | 122 +------- .../pmulabs/screensMobile/ReportScreen.kt | 280 ++++++++++++++++++ .../viewModels/AppViewModelProvider.kt | 3 + .../viewModels/ArticlePageScreenViewModel.kt | 1 + .../pmulabs/viewModels/ReportViewModel.kt | 47 +++ server/data.json | 225 ++++++++++++++ server/package.json | 2 +- server/report.js | 44 +++ 20 files changed, 667 insertions(+), 124 deletions(-) create mode 100644 app/src/main/java/com/example/pmulabs/api/model/ReportRemote.kt create mode 100644 app/src/main/java/com/example/pmulabs/screensMobile/ReportScreen.kt create mode 100644 app/src/main/java/com/example/pmulabs/viewModels/ReportViewModel.kt create mode 100644 server/data.json create mode 100644 server/report.js diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index a8a563b..96a1bca 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -12,6 +12,6 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 3d1e84f..cb41a95 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,9 +1,6 @@ + - - + - - \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/MainActivity.kt b/app/src/main/java/com/example/pmulabs/MainActivity.kt index aa660d7..ccbac64 100644 --- a/app/src/main/java/com/example/pmulabs/MainActivity.kt +++ b/app/src/main/java/com/example/pmulabs/MainActivity.kt @@ -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)) } } } diff --git a/app/src/main/java/com/example/pmulabs/api/NewsPortalService.kt b/app/src/main/java/com/example/pmulabs/api/NewsPortalService.kt index 5fe2fb8..fb8809c 100644 --- a/app/src/main/java/com/example/pmulabs/api/NewsPortalService.kt +++ b/app/src/main/java/com/example/pmulabs/api/NewsPortalService.kt @@ -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 @GET("users") suspend fun getUsers(): List @GET("tags") diff --git a/app/src/main/java/com/example/pmulabs/api/model/ReportRemote.kt b/app/src/main/java/com/example/pmulabs/api/model/ReportRemote.kt new file mode 100644 index 0000000..7a2c8de --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/api/model/ReportRemote.kt @@ -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 +) \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/api/repository/RestUserRepository.kt b/app/src/main/java/com/example/pmulabs/api/repository/RestUserRepository.kt index 47cbbd9..2851f07 100644 --- a/app/src/main/java/com/example/pmulabs/api/repository/RestUserRepository.kt +++ b/app/src/main/java/com/example/pmulabs/api/repository/RestUserRepository.kt @@ -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 + { + return service.getReport(startDate,endDate) + } + override suspend fun updateUser(user: User) { user.id?.let { service.updateUser(it, user.toUserRemote()).toUser() } } diff --git a/app/src/main/java/com/example/pmulabs/basecomponents/navigate/BottomBarScreen.kt b/app/src/main/java/com/example/pmulabs/basecomponents/navigate/BottomBarScreen.kt index 797fea2..32c7d17 100644 --- a/app/src/main/java/com/example/pmulabs/basecomponents/navigate/BottomBarScreen.kt +++ b/app/src/main/java/com/example/pmulabs/basecomponents/navigate/BottomBarScreen.kt @@ -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 + ) } diff --git a/app/src/main/java/com/example/pmulabs/graphs/HomeNavGraph.kt b/app/src/main/java/com/example/pmulabs/graphs/HomeNavGraph.kt index 1768ada..de3de5d 100644 --- a/app/src/main/java/com/example/pmulabs/graphs/HomeNavGraph.kt +++ b/app/src/main/java/com/example/pmulabs/graphs/HomeNavGraph.kt @@ -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) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/graphs/RootNavGraph.kt b/app/src/main/java/com/example/pmulabs/graphs/RootNavGraph.kt index 29d6625..781f701 100644 --- a/app/src/main/java/com/example/pmulabs/graphs/RootNavGraph.kt +++ b/app/src/main/java/com/example/pmulabs/graphs/RootNavGraph.kt @@ -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) } } } diff --git a/app/src/main/java/com/example/pmulabs/room/repository/OfflineUserRepository.kt b/app/src/main/java/com/example/pmulabs/room/repository/OfflineUserRepository.kt index f765989..6902cf5 100644 --- a/app/src/main/java/com/example/pmulabs/room/repository/OfflineUserRepository.kt +++ b/app/src/main/java/com/example/pmulabs/room/repository/OfflineUserRepository.kt @@ -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 = userDao.getUserComms(idUser) override suspend fun getUserArticles(idUser: Int): List
= userDao.getUserArticles(idUser) override suspend fun getUserTags(idUser: Int): List = userDao.getUserTags(idUser) + override suspend fun getReport(startDate: Long, endDate: Long): List { + TODO("Not yet implemented") + } + } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/room/repository/UserRepository.kt b/app/src/main/java/com/example/pmulabs/room/repository/UserRepository.kt index 2c03030..db7f853 100644 --- a/app/src/main/java/com/example/pmulabs/room/repository/UserRepository.kt +++ b/app/src/main/java/com/example/pmulabs/room/repository/UserRepository.kt @@ -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 suspend fun getUserArticles(idUser: Int): List
suspend fun getUserTags(idUser: Int): List + suspend fun getReport(startDate: Long, endDate: Long):List } \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/MainScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/MainScreen.kt index ee2562f..0a88ebf 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/MainScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/MainScreen.kt @@ -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) } } diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/ProfileScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/ProfileScreen.kt index c676190..5a358e2 100644 --- a/app/src/main/java/com/example/pmulabs/screensMobile/ProfileScreen.kt +++ b/app/src/main/java/com/example/pmulabs/screensMobile/ProfileScreen.kt @@ -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)}) } } } diff --git a/app/src/main/java/com/example/pmulabs/screensMobile/ReportScreen.kt b/app/src/main/java/com/example/pmulabs/screensMobile/ReportScreen.kt new file mode 100644 index 0000000..f181ba0 --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/screensMobile/ReportScreen.kt @@ -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) { + + 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() + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/pmulabs/viewModels/AppViewModelProvider.kt b/app/src/main/java/com/example/pmulabs/viewModels/AppViewModelProvider.kt index 3d39aa4..02017a3 100644 --- a/app/src/main/java/com/example/pmulabs/viewModels/AppViewModelProvider.kt +++ b/app/src/main/java/com/example/pmulabs/viewModels/AppViewModelProvider.kt @@ -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) + } } } diff --git a/app/src/main/java/com/example/pmulabs/viewModels/ArticlePageScreenViewModel.kt b/app/src/main/java/com/example/pmulabs/viewModels/ArticlePageScreenViewModel.kt index 13c5393..425af87 100644 --- a/app/src/main/java/com/example/pmulabs/viewModels/ArticlePageScreenViewModel.kt +++ b/app/src/main/java/com/example/pmulabs/viewModels/ArticlePageScreenViewModel.kt @@ -44,6 +44,7 @@ class ArticlePageScreenViewModel( } } + var userList by mutableStateOf>(emptyList()) fun setUserList() { viewModelScope.launch(Dispatchers.IO) { diff --git a/app/src/main/java/com/example/pmulabs/viewModels/ReportViewModel.kt b/app/src/main/java/com/example/pmulabs/viewModels/ReportViewModel.kt new file mode 100644 index 0000000..30d5277 --- /dev/null +++ b/app/src/main/java/com/example/pmulabs/viewModels/ReportViewModel.kt @@ -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 = emptyList() +) \ No newline at end of file diff --git a/server/data.json b/server/data.json new file mode 100644 index 0000000..4806355 --- /dev/null +++ b/server/data.json @@ -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 + } + ] +} \ No newline at end of file diff --git a/server/package.json b/server/package.json index 1a051f8..729ba53 100644 --- a/server/package.json +++ b/server/package.json @@ -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": { }, diff --git a/server/report.js b/server/report.js new file mode 100644 index 0000000..2b237bc --- /dev/null +++ b/server/report.js @@ -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" }); + } +};