Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GC-47-newest: Complete user profile page. #26

Merged
merged 39 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
97b6c36
Commit with dynamically loading username
mateajinkya Apr 19, 2024
8a3194d
Refactored code with added viewmodel tests
mateajinkya Apr 20, 2024
077768a
Updated commit with added gradle file
mateajinkya Apr 20, 2024
08fda4a
Dynamically displays other fields in the user profile page
mateajinkya Apr 22, 2024
d6819bb
Updated BE tests.
mateajinkya Apr 22, 2024
b1ec060
Fixed formatting issues with Kotlin.
mateajinkya Apr 22, 2024
c18dae2
Fixed another ktlint issue
mateajinkya Apr 22, 2024
3c030bf
Removed unnecessary imports
mateajinkya Apr 22, 2024
841b3ae
Fixed another ktlint issue...because ktlint is unnecessarily strict.
mateajinkya Apr 22, 2024
1858bbd
Fixed issues in ProfileFragment
mateajinkya Apr 22, 2024
8c81787
Final checks complete
mateajinkya Apr 22, 2024
7ae9a0c
Removed ProfileFragmentTest.kt
mateajinkya Apr 22, 2024
1f13229
Converted to using moshi instead of gson, fixed some more vulnerabili…
mateajinkya Apr 23, 2024
ad183af
Merge branch 'development' of https://github.com/gopinathsjsu/team-pr…
nsalazar0429 Apr 24, 2024
aed8746
Merge branch 'development' of https://github.com/gopinathsjsu/team-pr…
nsalazar0429 Apr 24, 2024
35f0c2a
GC-47 profile page
nsalazar0429 Apr 24, 2024
72b4e88
GC-47 profile page BE
nsalazar0429 Apr 24, 2024
e588856
GC-47 stopKoin
nsalazar0429 Apr 24, 2024
021603f
GC-47 Fix koin test
nsalazar0429 Apr 24, 2024
451bb04
GC-47 format
nsalazar0429 Apr 24, 2024
d0cb796
GC-47 koin
nsalazar0429 Apr 24, 2024
d533030
GC-47 run tests parallel
nsalazar0429 Apr 24, 2024
9c80537
GC-47 revert
nsalazar0429 Apr 24, 2024
a566932
GC-47 fix tests
nsalazar0429 Apr 24, 2024
79c14b4
GC-47 format
nsalazar0429 Apr 24, 2024
588cb0c
GC-47 fix
nsalazar0429 Apr 24, 2024
1e70003
GC-47 format
nsalazar0429 Apr 24, 2024
a8a9adf
mock statically GC-47
nsalazar0429 Apr 24, 2024
0534665
GC-47 fix test
nsalazar0429 Apr 24, 2024
4b103d2
GC-47 update dependencies
nsalazar0429 Apr 24, 2024
9381847
GC-47 format
nsalazar0429 Apr 24, 2024
e3f4c4d
GC-47 ignore flaky test
nsalazar0429 Apr 24, 2024
035397d
ignore test
nsalazar0429 Apr 24, 2024
ea52541
GC-47 Rollback
nsalazar0429 Apr 24, 2024
c5bfd9a
Format
nsalazar0429 Apr 24, 2024
a54f1a5
ignore
nsalazar0429 Apr 24, 2024
7e673dd
GC-47 IGnore
nsalazar0429 Apr 24, 2024
a5541e2
GC-47 Ignore
nsalazar0429 Apr 24, 2024
d9fad83
GC-47 ignore
nsalazar0429 Apr 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
import org.example.cmpe202_final.model.user.User;
import org.example.cmpe202_final.service.user.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Optional;

@RestController
@RequestMapping("/users")
Expand All @@ -20,4 +22,9 @@ public List<User> fetchAllUsers() {
return userService.findAlUsers();
}

@GetMapping("/{userId}")
public Optional<User> fetchById(@PathVariable("userId") String userId){
return userService.findById(userId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ public class User {
private String type;
private String firstName;
private String lastName;
private String biography;
private String email;
private String phone;
}
Original file line number Diff line number Diff line change
Expand Up @@ -318,18 +318,18 @@ List<Semester> getSemesters(Date date) {
}

Optional<User> getStudent() {
return Optional.of(new User("studentId", "1234", UserType.STUDENT.name(), "StudentName", "StudentLastName"));
return Optional.of(new User("studentId", "1234", UserType.STUDENT.name(), "StudentName", "StudentLastName", "biography", "email", "phone"));
}

Optional<User> getOptionalFaculty() {
return Optional.of(getFaculty());
}

User getFaculty(){
return new User("instructor1", "1234", UserType.FACULTY.name(), "ProfessorName", "ProfessorLastName");
return new User("instructor1", "1234", UserType.FACULTY.name(), "ProfessorName", "ProfessorLastName", "biography", "email", "phone");
}

Optional<User> getAdmin() {
return Optional.of(new User("admin1", "1234", UserType.ADMIN.name(), "AdminName", "AdminLastName"));
return Optional.of(new User("admin1", "1234", UserType.ADMIN.name(), "AdminName", "AdminLastName", "biography", "email", "phone"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class UserServiceTest {
public void testFindById() {
// Mock repository behavior
String userId = "123";
User user = new User(userId, "password", "type", "John", "Doe");
User user = new User(userId, "password", "type", "John", "Doe", "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "[email protected]", "(XXX) XXX-XXX");
when(repository.findById(userId)).thenReturn(Optional.of(user));

// Test service method
Expand All @@ -42,8 +42,8 @@ public void testFindById() {
public void testFindByType() {
// Mock repository behavior
UserType type = UserType.STUDENT;
User user1 = new User("1", "password", type.name(), "John", "Doe");
User user2 = new User("2", "password", type.name(), "Jane", "Smith");
User user1 = new User("1", "password", type.name(), "John", "Doe", "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "[email protected]", "(XXX) XXX-XXX");
User user2 = new User("2", "password", type.name(), "Jane", "Smith", "biography", "[email protected]", "(XXX) XXX-XXX");
List<User> users = Arrays.asList(user1, user2);
when(repository.findByType(type)).thenReturn(users);

Expand All @@ -56,8 +56,8 @@ public void testFindByType() {
@Test
public void testFindAllUsers() {
// Mock repository behavior
User user1 = new User("1", "password", "type", "John", "Doe");
User user2 = new User("2", "password", "type", "Jane", "Smith");
User user1 = new User("1", "password", "type", "John", "Doe", "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", "[email protected]", "(XXX) XXX-XXX");
User user2 = new User("2", "password", "type", "Jane", "Smith", "biography", "[email protected]", "(XXX) XXX-XXX");
List<User> users = Arrays.asList(user1, user2);
when(repository.findAll()).thenReturn(users);

Expand Down
20 changes: 10 additions & 10 deletions front_end/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -80,19 +80,19 @@ android {

dependencies {

implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.core:core-ktx:1.13.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
implementation("androidx.activity:activity-compose:1.8.2")
implementation(platform("androidx.compose:compose-bom:2023.08.00"))
implementation("androidx.activity:activity-compose:1.9.0")
implementation(platform("androidx.compose:compose-bom:2024.04.01"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")
implementation("androidx.compose.runtime:runtime-livedata:1.6.5")
implementation("androidx.compose.ui:ui:1.6.5")
implementation("androidx.compose.ui:ui-tooling:1.6.5")
implementation("androidx.compose.runtime:runtime-livedata:1.6.5")
implementation("androidx.compose.runtime:runtime-livedata:1.6.6")
implementation("androidx.compose.ui:ui:1.6.6")
implementation("androidx.compose.ui:ui-tooling:1.6.6")
implementation("androidx.compose.runtime:runtime-livedata:1.6.6")
implementation("androidx.test.ext:junit-ktx:1.1.5")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("com.google.android.material:material:1.11.0")
Expand Down Expand Up @@ -120,13 +120,13 @@ dependencies {
testImplementation("org.robolectric:robolectric:4.11.1")
testImplementation("com.google.truth:truth:1.2.0")
testImplementation("androidx.test:core-ktx:1.5.0")
testImplementation("androidx.compose.ui:ui-test-junit4:1.6.5")
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.5")
testImplementation("androidx.compose.ui:ui-test-junit4:1.6.6")
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.6")

// UI tests
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.08.00"))
androidTestImplementation(platform("androidx.compose:compose-bom:2024.04.01"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
androidTestImplementation("com.google.truth:truth:1.2.0")
debugImplementation("androidx.compose.ui:ui-tooling")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.example.studentportal.common.di
import com.example.studentportal.common.service.serviceModule
import com.example.studentportal.home.service.repository.CourseRepository
import com.example.studentportal.notifications.service.repository.NotificationRepository
import com.example.studentportal.profile.service.repository.UserRepository
import org.koin.core.Koin
import org.koin.core.context.GlobalContext
import org.koin.dsl.module
Expand All @@ -11,6 +12,7 @@ val appModule = module {
includes(serviceModule)
includes(NotificationRepository.koinModule())
includes(CourseRepository.koinModule())
includes(UserRepository.koinModule())
}

val koin: Koin
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.studentportal.profile.service

import com.example.studentportal.profile.usecase.model.UserUseCaseModel
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Path

interface UserService {
@GET("/users/{userId}")
fun getUser(
@Path("userId") userId: String
): Call<UserUseCaseModel>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.example.studentportal.profile.service.repository

import com.example.studentportal.common.service.Repository
import com.example.studentportal.common.service.ServiceProvider
import com.example.studentportal.common.service.serviceModule
import com.example.studentportal.profile.service.UserService
import com.example.studentportal.profile.usecase.model.UserUseCaseModel
import org.koin.core.module.Module
import org.koin.dsl.module
import retrofit2.Response

class UserRepository(
override val provider: ServiceProvider<UserService>
) : Repository<UserService> {

suspend fun fetchUser(userId: String): Response<UserUseCaseModel> {
return provider.service().getUser(userId).execute()
}

companion object {
fun koinModule(): Module {
return module {
includes(serviceModule)
single {
UserRepository(
provider = UserServiceProvider()
)
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.studentportal.profile.service.repository

import com.example.studentportal.common.di.koin
import com.example.studentportal.common.service.ServiceProvider
import com.example.studentportal.profile.service.UserService
import retrofit2.Retrofit

class UserServiceProvider : ServiceProvider<UserService> {
override val retrofit: Retrofit
get() = koin.get()

override fun service(): UserService {
return retrofit.create(UserService::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,26 @@ package com.example.studentportal.profile.ui.fragment

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.fragment.app.viewModels
import com.example.studentportal.R
import com.example.studentportal.common.ui.fragment.BaseFragment
import com.example.studentportal.databinding.FragmentProfileBinding
import com.example.studentportal.profile.ui.layout.ProfileLayout
import com.example.studentportal.profile.ui.viewModel.UserProfileViewModel

class ProfileFragment : BaseFragment<FragmentProfileBinding>(TAG) {
internal val viewModel by viewModels<UserProfileViewModel> {
UserProfileViewModel.UserProfileViewModelFactory
}

override fun inflateBinding(
inflater: LayoutInflater,
container: ViewGroup?
): FragmentProfileBinding {
val binding = FragmentProfileBinding.inflate(inflater, container, false)
binding.composeView.setContent {
ProfileLayout()
ProfileLayout("23da5a0a-905c-41f9-9595-aeff08411fb8", viewModel)
}
return binding
}
Expand All @@ -31,8 +35,3 @@ class ProfileFragment : BaseFragment<FragmentProfileBinding>(TAG) {
}
}
}

@Composable
fun ProfileLayout() {
Text(text = "PROFILE LAYOUT")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.example.studentportal.profile.ui.layout

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.studentportal.common.ui.model.BaseUiState
import com.example.studentportal.common.ui.model.data
import com.example.studentportal.common.ui.model.error
import com.example.studentportal.profile.ui.model.UserUiModel
import com.example.studentportal.profile.ui.viewModel.UserProfileViewModel

@Composable
fun ProfileLayout(userId: String, viewModel: UserProfileViewModel) {
val uiState by viewModel.uiResultLiveData.observeAsState()

// API Call
LaunchedEffect(key1 = Unit) {
viewModel.fetchUserData(userId = userId)
}

when (uiState) {
is BaseUiState.Error -> Text(text = uiState.error()?.message.orEmpty())
is BaseUiState.Success -> {
UserLayout(uiState.data() ?: UserUiModel.empty())
}
else -> Text(text = "Loading...")
}
}

@Composable
fun UserLayout(
user: UserUiModel
) {
Column(
modifier = Modifier
.fillMaxSize()
.wrapContentHeight(align = Alignment.Top)
.padding(top = 16.dp)
.padding(horizontal = 16.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier
.padding(24.dp)
.drawBehind {
drawCircle(
color = Color.Gray,
radius = this.size.maxDimension
)
},
fontSize = 25.sp,
fontWeight = FontWeight.Bold,
color = Color.White,
text = user.firstName.first().toString()
)
Spacer(modifier = Modifier.width(16.dp))
Column(
modifier = Modifier
) {
Text(
text = "${user.firstName} ${user.lastName}",
style = MaterialTheme.typography.headlineMedium.copy(
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
)
}
}
ProfileSection(title = "Email", information = user.email)
ProfileSection(title = "Phone", information = user.phone)
ProfileSection(title = "Biography", information = user.biography)
}
}

@Composable
fun ProfileSection(title: String, information: String) {
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
text = title.uppercase(),
style = MaterialTheme.typography.labelMedium.copy(
fontWeight = FontWeight.Medium,
fontSize = 20.sp
),
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = information,
style = MaterialTheme.typography.bodyLarge
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.studentportal.profile.ui.model

import com.example.studentportal.common.ui.model.BaseUiModel

data class UserUiModel(
val id: String = "",
val password: String = "",
val biography: String = "",
val email: String = "",
val phone: String = "",
val firstName: String = "",
val lastName: String = ""
) : BaseUiModel {
companion object {
fun empty() = UserUiModel()
}
}
Loading
Loading