From 5929f0535d6d76fd96e1a1d47a47a6c8f0150377 Mon Sep 17 00:00:00 2001 From: Your GitHub Username Date: Sun, 24 Dec 2023 09:28:20 +0000 Subject: [PATCH] My requests page added --- .../resq/ui/views/screens/MyRequestsScreen.kt | 139 ++++++++++++++++++ .../resq/viewmodels/MyRequestsViewModel.kt | 52 +++++++ 2 files changed, 191 insertions(+) create mode 100644 resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/ui/views/screens/MyRequestsScreen.kt create mode 100644 resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/MyRequestsViewModel.kt diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/ui/views/screens/MyRequestsScreen.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/ui/views/screens/MyRequestsScreen.kt new file mode 100644 index 00000000..116c6d6f --- /dev/null +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/ui/views/screens/MyRequestsScreen.kt @@ -0,0 +1,139 @@ +package com.cmpe451.resq.ui.views.screens +import android.content.Context +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.TopAppBar +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +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.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import com.cmpe451.resq.data.models.Need +import com.cmpe451.resq.ui.theme.RequestColor +import com.cmpe451.resq.viewmodels.MyRequestsViewModel +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import java.text.SimpleDateFormat +import java.util.Locale + +fun convertToReadableDate(dateStr: String): String { + val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.getDefault()) + val outputFormat = SimpleDateFormat("dd MMM yyyy, HH:mm", Locale.getDefault()) + + return try { + val date = inputFormat.parse(dateStr) + date?.let { outputFormat.format(it) } ?: "Unknown Date" + } catch (e: Exception) { + "Invalid Date" + } +} + +@Composable +fun MyRequestsScreen(navController: NavController, appContext: Context) { + val viewModel: MyRequestsViewModel = viewModel() + val needs by viewModel.needs + val scrollState = rememberScrollState() + // A side effect to load the needs when the composable enters the composition + LaunchedEffect(key1 = true) { + viewModel.getNeeds(appContext) + } + + Scaffold( + topBar = { + TopAppBar( + title = { Text(text = "My Requests", color = RequestColor) }, + navigationIcon = { + IconButton(onClick = { navController.navigateUp() }) { + Icon(imageVector = Icons.Default.ArrowBack, contentDescription = "Back") + } + }, + backgroundColor = Color.White, + elevation = 4.dp, + contentColor = Color.Black + ) + } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scrollState) + .padding(paddingValues), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(16.dp)) + needs.forEachIndexed { index, need -> + RequestCard(viewModel, requestNumber = index + 1, request = need) + Spacer(modifier = Modifier.height(8.dp)) + } + } + } +} + +@Composable +fun RequestCard(viewModel: MyRequestsViewModel,requestNumber: Int, request: Need) { + var isSelected by remember { mutableStateOf(false) } + val categoryName = viewModel.getCategoryName(request.categoryTreeId.toInt()) + + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + .clickable { isSelected = !isSelected }, + elevation = if (isSelected) 4.dp else 0.dp, + shape = RoundedCornerShape(8.dp), + border = BorderStroke(width = if (isSelected) 2.dp else 0.dp, color = RequestColor) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(all = 16.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "#$requestNumber", + style = MaterialTheme.typography.body1, + color = Color(0xFF007BFF) + ) + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = categoryName, + style = MaterialTheme.typography.body1, + color = RequestColor + ) + } + if (isSelected) { + Text("Description: ${request.description}") + Text("Quantity: ${request.quantity}") + Text("Latitude: ${request.latitude}") + Text("Longitude: ${request.longitude}") + Text("Status: ${request.status}") + Text("Created Date: ${convertToReadableDate(request.createdDate)}") + } + } + } +} diff --git a/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/MyRequestsViewModel.kt b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/MyRequestsViewModel.kt new file mode 100644 index 00000000..fbe9858f --- /dev/null +++ b/resq/mobile/ResQ/app/src/main/java/com/cmpe451/resq/viewmodels/MyRequestsViewModel.kt @@ -0,0 +1,52 @@ +package com.cmpe451.resq.viewmodels + +import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.cmpe451.resq.data.models.CategoryTreeNode +import com.cmpe451.resq.data.models.Need +import com.cmpe451.resq.data.remote.ResqService +import kotlinx.coroutines.launch +import android.content.Context + +class MyRequestsViewModel : ViewModel() { + val needs = mutableStateOf>(emptyList()) + private val _categoryTree = mutableStateOf>(emptyList()) + suspend fun getNeeds(appContext: Context) { + val api = ResqService(appContext) + api.viewNeedsByUserId( + onSuccess = { needList -> + needs.value = needList + fetchCategoryTree(appContext) + }, + onError = { error -> + // Handle error + } + ) + } + + private fun fetchCategoryTree(context: Context) { + val api = ResqService(context) + viewModelScope.launch { + val response = api.getMainCategories() // Assuming this method exists and fetches the entire category tree + if (response.isSuccessful) { + _categoryTree.value = response.body() ?: emptyList() + } else { + // Handle error + } + } + } + + fun getCategoryName(categoryId: Int): String { + return findCategoryName(_categoryTree.value, categoryId) + } + + private fun findCategoryName(categoryList: List, categoryId: Int): String { + for (category in categoryList) { + if (category.id == categoryId) return category.data + val foundName = findCategoryName(category.children, categoryId) + if (foundName.isNotEmpty() && foundName != "Unknown Category") return foundName + } + return "Unknown Category" + } +}