diff --git a/.gitignore b/.gitignore index aa724b7..86f1fbc 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ .externalNativeBuild .cxx local.properties +/app/src/main/java/com/example/listifyjetapp/utils/constants/Constants.kt \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/components/FormDialog.kt b/app/src/main/java/com/example/listifyjetapp/components/FormDialog.kt new file mode 100644 index 0000000..9a40f38 --- /dev/null +++ b/app/src/main/java/com/example/listifyjetapp/components/FormDialog.kt @@ -0,0 +1,47 @@ +package com.example.listifyjetapp.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties + +@Composable +fun FormDialog( + onDismiss: () -> Unit = {}, + onConfirm: () -> Unit = {} +) { + Dialog ( + onDismissRequest = onDismiss, + properties = DialogProperties(usePlatformDefaultWidth = false) + ) { + Card( + elevation = CardDefaults.cardElevation(defaultElevation = 6.dp), + shape = RoundedCornerShape(15.dp), + modifier = Modifier.fillMaxWidth(0.95f) + ) { + Column( + modifier = Modifier.fillMaxWidth().padding(15.dp) + //verticalArrangement = Arrangement.SpaceBy(25.dp) + ) { + Text( + text = "Add new list", + textAlign = TextAlign.Center + ) + + Column { + Row { } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/model/ListModel.kt b/app/src/main/java/com/example/listifyjetapp/model/ListModel.kt index 09016c6..d539814 100644 --- a/app/src/main/java/com/example/listifyjetapp/model/ListModel.kt +++ b/app/src/main/java/com/example/listifyjetapp/model/ListModel.kt @@ -1,11 +1,27 @@ package com.example.listifyjetapp.model -import java.time.LocalDateTime +import com.google.gson.annotations.SerializedName + +data class BaseList( + val id: Int, + val name: String, + val share: Boolean, + //val sharedCode: String, // can be UUID or String + //val createdAt: LocalDateTime + @SerializedName("shared_code") val sharedCode: String, + @SerializedName("created_at") val createdAt: String, +) + +data class ListName ( + val name: String +) data class ListModel( val id: Int, val name: String, val share: Boolean, - val shareCode: String, // can be UUID or String - val createdAt: LocalDateTime + @SerializedName("shared_code") val sharedCode: String, + @SerializedName("created_at") val createdAt: String, + @SerializedName("item_count") val itemCount: Int, + @SerializedName("shared_with") val sharedWith: List ) diff --git a/app/src/main/java/com/example/listifyjetapp/model/User.kt b/app/src/main/java/com/example/listifyjetapp/model/User.kt new file mode 100644 index 0000000..706255e --- /dev/null +++ b/app/src/main/java/com/example/listifyjetapp/model/User.kt @@ -0,0 +1,17 @@ +package com.example.listifyjetapp.model + +import com.google.gson.annotations.SerializedName + +data class User( + val id: Int, + val username: String, + val email: String, + val password: String, + @SerializedName("created_at") val createdAt: String +) + +data class SharedUsers( + @SerializedName("user_id") val userId: Int, + val username: String, +) + diff --git a/app/src/main/java/com/example/listifyjetapp/network/ListsAPI.kt b/app/src/main/java/com/example/listifyjetapp/network/ListsAPI.kt index c408725..d928a9e 100644 --- a/app/src/main/java/com/example/listifyjetapp/network/ListsAPI.kt +++ b/app/src/main/java/com/example/listifyjetapp/network/ListsAPI.kt @@ -1,8 +1,12 @@ package com.example.listifyjetapp.network import com.example.listifyjetapp.model.ListModel +import com.example.listifyjetapp.model.ListName +import com.example.listifyjetapp.model.User +import retrofit2.http.Body import retrofit2.http.DELETE import retrofit2.http.GET +import retrofit2.http.PATCH import retrofit2.http.POST import retrofit2.http.Path import javax.inject.Singleton @@ -10,27 +14,27 @@ import javax.inject.Singleton @Singleton interface ListifyAPI { // =============================================== Users ======================================= -// @PATCH("users/{user_id}") -// suspend fun patchUserById(@Path("user_id") userId: Int): User -// -// @POST("users") -// suspend fun createUser(): User -// -// @GET("users/check_username/{username}") -// suspend fun getUserByUsername(@Path("/username") username: String ): User -// -// @GET("users/email/{email}") -// suspend fun getUserByUserEmail(@Path("/email") email: String ): User -// -// @GET("users/users") -// suspend fun getAllUsers(): List + @PATCH("users/{user_id}") + suspend fun patchUserById(@Path("user_id") userId: Int): User + + @POST("users") + suspend fun createUser(): User + + @GET("users/check_username/{username}") + suspend fun getUserByUsername(@Path("/username") username: String ): User + + @GET("users/email/{email}") + suspend fun getUserByUserEmail(@Path("/email") email: String ): User + + @GET("users/users") + suspend fun getAllUsers(): List // =============================================== User Lists ================================== -// @GET("ul/shared-user/{list_id}/{user_id}") -// suspend fun getSharedUsers( -// @Path("user_id") userId: Int, -// @Path("list_id") listId: Int -// ): List + @GET("ul/shared-user/{list_id}/{user_id}") + suspend fun getSharedUsers( + @Path("user_id") userId: Int, + @Path("list_id") listId: Int + ): List @GET("ul/{user_id}") suspend fun getListsByUser(@Path("user_id") userId: Int): List @@ -56,7 +60,10 @@ interface ListifyAPI { // Post new list @POST("lists/{user_id}") - suspend fun createList(@Path("user_id") userId: Int) {} + suspend fun insertList( + @Path("user_id") userId: Int, + @Body request: ListName + ): ListModel // Get items by list id @GET("lists/{list_id}") diff --git a/app/src/main/java/com/example/listifyjetapp/repository/ListsRepository.kt b/app/src/main/java/com/example/listifyjetapp/repository/ListsRepository.kt index 0130db4..f54e1e2 100644 --- a/app/src/main/java/com/example/listifyjetapp/repository/ListsRepository.kt +++ b/app/src/main/java/com/example/listifyjetapp/repository/ListsRepository.kt @@ -2,6 +2,7 @@ package com.example.listifyjetapp.repository import com.example.listifyjetapp.data.ListifyResult import com.example.listifyjetapp.model.ListModel +import com.example.listifyjetapp.model.ListName import com.example.listifyjetapp.network.ListifyAPI import javax.inject.Inject @@ -15,4 +16,22 @@ class ListsRepository @Inject constructor(private val api: ListifyAPI) { return ListifyResult.Failure(e.message ?: "Error fetching lists") } } + + suspend fun insertListByUser(userId: Int, newListData: ListName): ListifyResult { + try { + val response = api.insertList(userId, newListData) + val formatedResponse = ListModel ( + id = response.id, + name = response.name, + share = response.share, + sharedCode = response.sharedCode, + createdAt = response.createdAt, + itemCount = 0, + sharedWith= emptyList() + ) + return ListifyResult.Success(data = formatedResponse) + } catch (e:Exception) { + return ListifyResult.Failure(e.message ?: "Error creating new list") + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/ui/navigation/ListifyNavigation.kt b/app/src/main/java/com/example/listifyjetapp/ui/navigation/ListifyNavigation.kt index 039c9fa..8a11bf7 100644 --- a/app/src/main/java/com/example/listifyjetapp/ui/navigation/ListifyNavigation.kt +++ b/app/src/main/java/com/example/listifyjetapp/ui/navigation/ListifyNavigation.kt @@ -5,6 +5,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.example.listifyjetapp.ui.screens.lists.ListifyListsScreen +import com.example.listifyjetapp.ui.screens.newList.ListifyNewListScreen import com.example.listifyjetapp.ui.screens.splash.ListifySplashScreen @@ -26,7 +27,12 @@ fun ListifyNavigation() { // TODO: Define a navigation route for ListsScreen composable(ListifyScreens.ListsScreen.route) { - ListifyListsScreen() + ListifyListsScreen(navController = navController) + } + + // TODO: Define a navigation route for NewListScreen + composable(ListifyScreens.NewListScreen.route) { + ListifyNewListScreen(navController = navController) } // TODO: Define a navigation route for DetailScreen diff --git a/app/src/main/java/com/example/listifyjetapp/ui/navigation/ListifyScreens.kt b/app/src/main/java/com/example/listifyjetapp/ui/navigation/ListifyScreens.kt index b1914cb..880719b 100644 --- a/app/src/main/java/com/example/listifyjetapp/ui/navigation/ListifyScreens.kt +++ b/app/src/main/java/com/example/listifyjetapp/ui/navigation/ListifyScreens.kt @@ -6,7 +6,8 @@ enum class ListifyScreens(val route: String) { DetailScreen("detail"), ProfileScreen("profile"), LoginScreen("login"), - SignupScreen("signup") + SignupScreen("signup"), + NewListScreen("new-list") } // navController.navigate(ListifyScreens.LoginScreen.route) \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListRow.kt b/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListRow.kt new file mode 100644 index 0000000..9fd6113 --- /dev/null +++ b/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListRow.kt @@ -0,0 +1,102 @@ +package com.example.listifyjetapp.ui.screens.lists + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight +import androidx.compose.material.icons.filled.Share +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.listifyjetapp.model.ListModel +import com.example.listifyjetapp.ui.theme.ListifyColor + +@Composable +fun ListRow( + list: ListModel, + //viewModel: User +) { + Row( + modifier = Modifier + .clickable { } + .padding(vertical = 16.dp) + .fillMaxWidth() + .background(Color.Transparent) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + // List info + Column( + modifier = Modifier.padding(horizontal = 8.dp), + + ) { + Text( + text = list.name, + color = ListifyColor.TextBlack, + fontSize = 20.sp, + //fontWeight = FontWeight.SemiBold + ) + + if (list.share) { + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + modifier = Modifier.size(16.dp), + tint = ListifyColor.IconGreen, + imageVector = Icons.Default.Share, + contentDescription = "Shared with" + ) + Text( + text = list.sharedWith.take(3).joinToString(", ") { it.username }, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = ListifyColor.TextGrey, + fontSize = 16.sp + ) + } + } + + Text( + text = list.createdAt, + color = ListifyColor.TextGrey, + fontSize = 16.sp + ) + } + // List setting menu + + Row() { + Text( + text = list.itemCount.toString(), + color = ListifyColor.TextGrey, + fontSize = 16.sp + ) + + Icon( + imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, + contentDescription = "Forward icon" + ) + + } + + } + + } + HorizontalDivider() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListifyListsScreen.kt b/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListifyListsScreen.kt index 0bff383..832f387 100644 --- a/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListifyListsScreen.kt +++ b/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListifyListsScreen.kt @@ -1,41 +1,103 @@ package com.example.listifyjetapp.ui.screens.lists +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController import com.example.listifyjetapp.R +import com.example.listifyjetapp.ui.screens.lists.ListRow +import com.example.listifyjetapp.ui.navigation.ListifyScreens +import com.example.listifyjetapp.utils.filterListItems +import com.example.listifyjetapp.widgets.ListifySearchBar +import com.example.listifyjetapp.widgets.ListifyTopBar @Composable fun ListifyListsScreen( + navController: NavController, viewModel: ListsViewModel = hiltViewModel() ) { LaunchedEffect(Unit) { viewModel.getUserLists(4) } - Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + + Scaffold( + modifier = Modifier.fillMaxSize(), + topBar = { ListifyTopBar( + title = "Lists", + isListsScreen = true, + rightIcon = Icons.Default.Add, + onRightButtonClick = { + navController.navigate(ListifyScreens.NewListScreen.route) + } + ) } + ) { innerPadding -> + Surface( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) + modifier = Modifier.fillMaxSize().padding(innerPadding) ) { - if (viewModel.lists.isEmpty()) { - Text(text = stringResource(R.string.no_lists)) - } else { - LazyColumn { - items(viewModel.lists) { list -> - Text(text = list.name) + Column( + modifier = Modifier.padding(horizontal = 16.dp), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.CenterHorizontally + ) { + + val searchTextState = remember { mutableStateOf("") } + val keyboardController = LocalSoftwareKeyboardController.current + + ListifySearchBar( + searchTextValue = searchTextState, + onValueChange = {searchTextState.value = it}, + keyboardAction = KeyboardActions{ + // Trigger search logic or hide keyboard + searchTextState.value.trim() // perform the search + keyboardController?.hide() // hide keyboard + } + ) + + if (viewModel.isLoading.value) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } else if (viewModel.lists.isEmpty()) { + Text(text = stringResource(R.string.no_lists)) + } else { + LazyColumn(modifier = Modifier.padding( + vertical = 16.dp, + horizontal = 8.dp + )){ + // Filter lists by search input + val results = filterListItems(searchTextState.value, viewModel.lists) + + items(results) {list -> + ListRow(list) + } } } } + } } - } \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListsViewModel.kt b/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListsViewModel.kt index 894316b..16b6c2b 100644 --- a/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListsViewModel.kt +++ b/app/src/main/java/com/example/listifyjetapp/ui/screens/lists/ListsViewModel.kt @@ -1,29 +1,58 @@ package com.example.listifyjetapp.ui.screens.lists import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.navigation.NavController import com.example.listifyjetapp.data.ListifyResult import com.example.listifyjetapp.model.ListModel +import com.example.listifyjetapp.model.ListName import com.example.listifyjetapp.repository.ListsRepository import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class ListsViewModel @Inject constructor(private val repository: ListsRepository): ViewModel() { val lists = mutableStateListOf() + var isLoading = mutableStateOf(false) + + private val _navigateBack = MutableStateFlow(false) + val navigateBack = _navigateBack.asStateFlow() fun getUserLists(userId: Int) { viewModelScope.launch { - val result = repository.getUserLists(userId = userId) - when(result) { - is ListifyResult.Success -> { - lists.clear() - lists.addAll(result.data) + isLoading.value = true + val result = repository.getUserLists(userId = userId) + when (result) { + is ListifyResult.Success -> { + lists.clear() + lists.addAll(result.data) + } + + is ListifyResult.Failure -> Unit } - is ListifyResult.Failure -> Unit + isLoading.value = false + } + } + + fun insertListByUser(userId: Int, newListName: ListName) + = viewModelScope.launch { + isLoading.value = true + val result = repository.insertListByUser(userId, newListName) + when (result) { + is ListifyResult.Success -> { + _navigateBack.value = true } + is ListifyResult.Failure -> Unit } + isLoading.value = false + } + + fun navigationComplete() { + _navigateBack.value = false } } \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/ui/screens/newList/ListifyNewListScreen.kt b/app/src/main/java/com/example/listifyjetapp/ui/screens/newList/ListifyNewListScreen.kt new file mode 100644 index 0000000..d71b47b --- /dev/null +++ b/app/src/main/java/com/example/listifyjetapp/ui/screens/newList/ListifyNewListScreen.kt @@ -0,0 +1,84 @@ +package com.example.listifyjetapp.ui.screens.newList + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding + +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController +import com.example.listifyjetapp.model.ListName +import com.example.listifyjetapp.ui.screens.lists.ListsViewModel +import com.example.listifyjetapp.widgets.FormInputField +import com.example.listifyjetapp.widgets.ListifyTopBar + +@Composable +fun ListifyNewListScreen( + navController: NavController, + viewModel: ListsViewModel = hiltViewModel() +) { + + val formTextState = remember { mutableStateOf("") } + + LaunchedEffect(viewModel.navigateBack) { + viewModel.navigateBack.collect { navigateBack -> + if (navigateBack) { + navController.popBackStack() + viewModel.navigationComplete() + } + } + } + + fun onSaveClick() { + val listName = ListName(name = formTextState.value) + viewModel.insertListByUser(4, listName) + } + + Scaffold( + modifier = Modifier.fillMaxSize(), + topBar = { ListifyTopBar( + title = "New List", + isListsScreen = false, + //goBackIcon = Icons.AutoMirrored.Filled.ArrowBack, + onGoBackButtonClicked = {navController.popBackStack()}, + leftText = "Cancel", + rightText = "Save", + onRightButtonClick = { + // TODO: save new list + onSaveClick() + } + ) } + ) { innerPadding -> + + Surface( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + ) { + + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(25.dp) + ) { + + Column() { + FormInputField( + textState=formTextState, + onValueChange={ formTextState.value = it } + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/ui/screens/newList/NewListViewModel.kt b/app/src/main/java/com/example/listifyjetapp/ui/screens/newList/NewListViewModel.kt new file mode 100644 index 0000000..86b3cb6 --- /dev/null +++ b/app/src/main/java/com/example/listifyjetapp/ui/screens/newList/NewListViewModel.kt @@ -0,0 +1,9 @@ +package com.example.listifyjetapp.ui.screens.newList + +import com.example.listifyjetapp.repository.ListsRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +//@HiltViewModel +//class NewListViewModel @Inject constructor(private val repository: ListsRepository){ +//} \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/ui/theme/Color.kt b/app/src/main/java/com/example/listifyjetapp/ui/theme/Color.kt index 6b65d4a..d525909 100644 --- a/app/src/main/java/com/example/listifyjetapp/ui/theme/Color.kt +++ b/app/src/main/java/com/example/listifyjetapp/ui/theme/Color.kt @@ -12,6 +12,8 @@ val Pink40 = Color(0xFF7D5260) object ListifyColor { val SplashYellow = Color(0xFFFFCA3A) + val TextBlack = Color(0xff000000) val TextDark = Color(0xff3e4e50) val TextGrey = Color(0xff858585) + val IconGreen = Color(0xff0cc25f) } diff --git a/app/src/main/java/com/example/listifyjetapp/utils/filterListItems.kt b/app/src/main/java/com/example/listifyjetapp/utils/filterListItems.kt new file mode 100644 index 0000000..e1c4f94 --- /dev/null +++ b/app/src/main/java/com/example/listifyjetapp/utils/filterListItems.kt @@ -0,0 +1,14 @@ +package com.example.listifyjetapp.utils + +import android.util.Log +import com.example.listifyjetapp.model.ListModel + +fun filterListItems(input: String, listItems: List): List { + + return if (input.isEmpty()) { + listItems + } else { + listItems.filter { it.name.contains(input, ignoreCase = true) } + } + +} diff --git a/app/src/main/java/com/example/listifyjetapp/widgets/FormInputField.kt b/app/src/main/java/com/example/listifyjetapp/widgets/FormInputField.kt new file mode 100644 index 0000000..d74a32d --- /dev/null +++ b/app/src/main/java/com/example/listifyjetapp/widgets/FormInputField.kt @@ -0,0 +1,46 @@ +package com.example.listifyjetapp.widgets + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import com.example.listifyjetapp.ui.theme.ListifyColor + +@Composable +fun FormInputField( + textState: MutableState, + onValueChange: (String) -> Unit, +) { + + TextField( + modifier = Modifier.fillMaxWidth(), + + shape = RoundedCornerShape(12.dp), + colors = TextFieldDefaults.colors( + unfocusedIndicatorColor = Color.White, + focusedIndicatorColor = Color.White, + disabledIndicatorColor = Color.Transparent, + + unfocusedLabelColor = ListifyColor.TextDark, + unfocusedContainerColor = ListifyColor.TextDark.copy(0.1f), + focusedContainerColor = ListifyColor.TextDark.copy(0.1f), + cursorColor = ListifyColor.TextDark, + ), + singleLine = true, + maxLines = 1, + + value = textState.value, + onValueChange = onValueChange, + placeholder = { Text(text="e.g., grocery list") }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), // Sets the keyboard to normal text input. + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/widgets/ListifySearchBar.kt b/app/src/main/java/com/example/listifyjetapp/widgets/ListifySearchBar.kt new file mode 100644 index 0000000..8443b5f --- /dev/null +++ b/app/src/main/java/com/example/listifyjetapp/widgets/ListifySearchBar.kt @@ -0,0 +1,61 @@ +package com.example.listifyjetapp.widgets + + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.text.input.InputTransformation.Companion.keyboardOptions +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.unit.dp +import com.example.listifyjetapp.ui.theme.ListifyColor + + +@Composable +fun ListifySearchBar( + searchTextValue: MutableState, + onValueChange: (String) -> Unit, + imeAction: ImeAction = ImeAction.Next, // what happen when you press "Next" + keyboardAction: KeyboardActions = KeyboardActions.Default // what to do when an action is triggered +) { + TextField( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = TextFieldDefaults.colors( + unfocusedIndicatorColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + disabledIndicatorColor = Color.Transparent, + + unfocusedLabelColor = ListifyColor.TextDark.copy(0.1f), + focusedContainerColor = ListifyColor.TextDark.copy(0.1f), + cursorColor = ListifyColor.TextDark, + ), + singleLine = true, + maxLines = 1, + leadingIcon = { + Icon( + imageVector = Icons.Default.Search, + contentDescription = "Search icon", + ) + }, + + value = searchTextValue.value, + onValueChange = onValueChange, + placeholder = { Text(text="Search") }, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text), // Sets the keyboard to normal text input. + keyboardActions = keyboardAction, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/listifyjetapp/widgets/ListifyTopBar.kt b/app/src/main/java/com/example/listifyjetapp/widgets/ListifyTopBar.kt new file mode 100644 index 0000000..4d88598 --- /dev/null +++ b/app/src/main/java/com/example/listifyjetapp/widgets/ListifyTopBar.kt @@ -0,0 +1,87 @@ +package com.example.listifyjetapp.widgets + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults.topAppBarColors +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.listifyjetapp.ui.theme.ListifyColor + +@OptIn(ExperimentalMaterial3Api::class) +@Preview +@Composable +fun ListifyTopBar( + title: String = "Screen Tittle", + isListsScreen: Boolean = true, + goBackIcon: ImageVector? = null, + leftText: String? = "", + rightIcon: ImageVector? = null, + rightText: String? = "", + onGoBackButtonClicked: () -> Unit = {}, + onRightButtonClick: () -> Unit = {} +) { + + CenterAlignedTopAppBar( + //modifier = Modifier.shadow(elevation = 5.dp), + colors = topAppBarColors(containerColor = Color.Transparent), + title = { + Text(text = title, fontWeight = FontWeight.ExtraBold, fontSize = 24.sp) + }, + + actions = { + if (rightIcon != null) { + IconButton(onClick = { onRightButtonClick() }) { + Icon( + modifier = Modifier.size(24.dp), + imageVector = rightIcon, //Icons.Default.Add, + contentDescription = "Add icon" + ) + } + } + + if (rightText.toString().isNotEmpty() && rightText != null) { + Text( + text = rightText, + modifier = Modifier.padding(horizontal = 16.dp).clickable { onRightButtonClick() }, + fontSize = 20.sp, + color = ListifyColor.SplashYellow + ) + } + }, + + navigationIcon = { + if (goBackIcon != null) { + Icon( + imageVector = goBackIcon, + contentDescription = "GO back icon", + modifier = Modifier.size(24.dp).clickable { onGoBackButtonClicked.invoke() } + ) + } + if (leftText.toString().isNotEmpty() && leftText != null) { + Text( + text = leftText, + modifier = Modifier + .padding(horizontal = 16.dp) + .clickable { onGoBackButtonClicked.invoke() }, + fontSize = 20.sp, + color = ListifyColor.SplashYellow + ) + } + + } + ) +} \ No newline at end of file