Skip to content

Commit 39b0ea7

Browse files
Merge pull request #671 from pennlabs/minor-ui-changes
Add reusable error card to dining insights and handled RA/no plan users as separate cases
2 parents dfe3d17 + 5a08459 commit 39b0ea7

16 files changed

Lines changed: 365 additions & 281 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package com.pennapps.labs.pennmobile.compose.presentation.components.error
2+
3+
import SFProDisplayMedium
4+
import android.content.res.Configuration
5+
import androidx.compose.foundation.layout.Column
6+
import androidx.compose.foundation.layout.fillMaxWidth
7+
import androidx.compose.foundation.layout.padding
8+
import androidx.compose.material3.Card
9+
import androidx.compose.material3.CardDefaults
10+
import androidx.compose.material3.MaterialTheme
11+
import androidx.compose.material3.Text
12+
import androidx.compose.runtime.Composable
13+
import androidx.compose.ui.Alignment
14+
import androidx.compose.ui.Modifier
15+
import androidx.compose.ui.text.style.TextAlign
16+
import androidx.compose.ui.tooling.preview.Preview
17+
import androidx.compose.ui.unit.dp
18+
import com.pennapps.labs.pennmobile.compose.presentation.theme.AppTheme
19+
20+
@Composable
21+
fun ErrorCard(
22+
errorMessage: String,
23+
modifier: Modifier = Modifier,
24+
) {
25+
Card(
26+
modifier = modifier,
27+
shape = MaterialTheme.shapes.small,
28+
colors =
29+
CardDefaults.cardColors(
30+
containerColor = MaterialTheme.colorScheme.surface,
31+
contentColor = MaterialTheme.colorScheme.onBackground,
32+
),
33+
) {
34+
Text(
35+
text = errorMessage,
36+
color = MaterialTheme.colorScheme.onBackground,
37+
fontFamily = SFProDisplayMedium,
38+
modifier =
39+
Modifier
40+
.fillMaxWidth()
41+
.align(Alignment.CenterHorizontally)
42+
.padding(vertical = 24.dp, horizontal = 12.dp),
43+
textAlign = TextAlign.Center,
44+
)
45+
}
46+
}
47+
48+
@Preview(
49+
name = "Light Mode",
50+
showBackground = true,
51+
uiMode = Configuration.UI_MODE_NIGHT_NO,
52+
)
53+
@Preview(
54+
name = "Dark Mode",
55+
showBackground = true,
56+
uiMode = Configuration.UI_MODE_NIGHT_YES,
57+
)
58+
@Composable
59+
private fun PreviewErrorCard() {
60+
AppTheme {
61+
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
62+
ErrorCard(
63+
UserDisplayErrors.PAST_BALANCES_NOT_AVAILABLE,
64+
Modifier
65+
.padding(vertical = 12.dp)
66+
.fillMaxWidth(0.95f),
67+
)
68+
}
69+
}
70+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.pennapps.labs.pennmobile.compose.presentation.components.error
2+
3+
object UserDisplayErrors {
4+
const val CAMPUS_EXPRESS_DOWN =
5+
"Dining prediction graphs are down due to an error with Campus Express. " +
6+
"We are working on a fix and will release it ASAP."
7+
8+
const val PAST_BALANCES_NOT_AVAILABLE =
9+
"Dining prediction graphs are unavailable since you are either not on a dining plan " +
10+
"or an RA on a special plan that we can't track."
11+
}

PennMobile/src/main/java/com/pennapps/labs/pennmobile/compose/presentation/components/AppSnackBar.kt renamed to PennMobile/src/main/java/com/pennapps/labs/pennmobile/compose/presentation/components/snackbar/AppSnackBar.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.pennapps.labs.pennmobile.compose.presentation.components
1+
package com.pennapps.labs.pennmobile.compose.presentation.components.snackbar
22

33
import android.util.Log
44
import androidx.compose.foundation.background

PennMobile/src/main/java/com/pennapps/labs/pennmobile/compose/presentation/theme/Styles.kt renamed to PennMobile/src/main/java/com/pennapps/labs/pennmobile/compose/presentation/theme/CustomTextStyles.kt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,21 @@ import androidx.compose.ui.text.TextStyle
66
import androidx.compose.ui.text.font.FontWeight
77
import androidx.compose.ui.unit.sp
88

9-
109
/**
1110
* Object to hold custom TextStyles for consistent typography across the app.
1211
*/
1312
object CustomTextStyles {
14-
1513
/**
1614
* Text style for all headers in the dining hall screen
17-
*/
15+
*/
1816
@Composable
19-
fun DiningHallsHeader(): TextStyle {
20-
return TextStyle(
17+
fun DiningHallsHeader(): TextStyle =
18+
TextStyle(
2119
fontFamily = GilroyFontFamily,
2220
fontWeight = FontWeight.ExtraBold,
2321
fontSize = 20.sp,
2422
color = MaterialTheme.colorScheme.onSurfaceVariant,
2523
lineHeight = 28.sp,
26-
letterSpacing = 0.sp
24+
letterSpacing = 0.sp,
2725
)
28-
}
29-
30-
}
26+
}

PennMobile/src/main/java/com/pennapps/labs/pennmobile/compose/presentation/theme/Fonts.kt

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,23 @@ private val GoogleSansFont = GoogleFont("Google Sans")
2121
// Cabin Font Family
2222
val cabinFontFamily =
2323
FontFamily(
24-
androidx.compose.ui.text.googlefonts.Font(googleFont = CabinFont, fontProvider = provider, weight = FontWeight.Normal), // Regular
25-
androidx.compose.ui.text.googlefonts.Font(googleFont = CabinFont, fontProvider = provider, weight = FontWeight.Medium),
26-
androidx.compose.ui.text.googlefonts.Font(googleFont = CabinFont, fontProvider = provider, weight = FontWeight.SemiBold),
24+
androidx.compose.ui.text.googlefonts
25+
.Font(googleFont = CabinFont, fontProvider = provider, weight = FontWeight.Normal), // Regular
26+
androidx.compose.ui.text.googlefonts
27+
.Font(googleFont = CabinFont, fontProvider = provider, weight = FontWeight.Medium),
28+
androidx.compose.ui.text.googlefonts
29+
.Font(googleFont = CabinFont, fontProvider = provider, weight = FontWeight.SemiBold),
2730
)
2831

2932
// Google Sans Font Family
3033
val googleSansFontFamily =
3134
FontFamily(
32-
androidx.compose.ui.text.googlefonts.Font(googleFont = GoogleSansFont, fontProvider = provider, weight = FontWeight.Normal), // Regular
33-
androidx.compose.ui.text.googlefonts.Font(googleFont = GoogleSansFont, fontProvider = provider, weight = FontWeight.Medium),
34-
androidx.compose.ui.text.googlefonts.Font(googleFont = GoogleSansFont, fontProvider = provider, weight = FontWeight.SemiBold),
35+
androidx.compose.ui.text.googlefonts
36+
.Font(googleFont = GoogleSansFont, fontProvider = provider, weight = FontWeight.Normal), // Regular
37+
androidx.compose.ui.text.googlefonts
38+
.Font(googleFont = GoogleSansFont, fontProvider = provider, weight = FontWeight.Medium),
39+
androidx.compose.ui.text.googlefonts
40+
.Font(googleFont = GoogleSansFont, fontProvider = provider, weight = FontWeight.SemiBold),
3541
)
3642

3743
val GilroyFontFamily =

PennMobile/src/main/java/com/pennapps/labs/pennmobile/di/AppModule.kt

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,3 @@
1-
/**
2-
* @file AppModule.kt
3-
* @brief Hilt module for providing application-wide singleton dependencies.
4-
*
5-
* First ever Dagger Hilt module written for Penn Mobile.
6-
*
7-
* This module is responsible for providing foundational objects that are used across
8-
* the entire application lifecycle, such as SharedPreferences and a global CoroutineScope.
9-
* All dependencies provided here are scoped as singletons.
10-
*
11-
* Created by Andrew Chelimo on 2/11/2025
12-
*/
131
package com.pennapps.labs.pennmobile.di
142

153
import android.content.Context
@@ -26,6 +14,17 @@ import kotlinx.coroutines.SupervisorJob
2614
import javax.inject.Qualifier
2715
import javax.inject.Singleton
2816

17+
/**
18+
* @brief Hilt module for providing application-wide singleton dependencies.
19+
*
20+
* First ever Dagger Hilt module written for Penn Mobile.
21+
*
22+
* This module is responsible for providing foundational objects that are used across
23+
* the entire application lifecycle, such as SharedPreferences and a global CoroutineScope.
24+
* All dependencies provided here are scoped as singletons.
25+
*
26+
* Created by Andrew Chelimo on 2/11/2025
27+
*/
2928
@Module
3029
@InstallIn(SingletonComponent::class)
3130
object AppModule {
@@ -56,4 +55,4 @@ object AppModule {
5655
*/
5756
@Qualifier
5857
@Retention(AnnotationRetention.RUNTIME)
59-
annotation class AppScope
58+
annotation class AppScope

PennMobile/src/main/java/com/pennapps/labs/pennmobile/di/NetworkModule.kt

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,3 @@
1-
/**
2-
* @file NetworkModule.kt
3-
* @brief Hilt module for providing network-related singleton components.
4-
*
5-
* This module is responsible for setting up and providing all dependencies required for
6-
* network operations throughout the application. It includes the configuration for Gson,
7-
* OkHttpClient, Retrofit, and the specific API service interfaces. All dependencies
8-
* provided here are scoped as singletons to ensure a single, shared instance is used
9-
* across the app.
10-
*/
111
package com.pennapps.labs.pennmobile.di
122

133
import com.google.gson.Gson
@@ -37,6 +27,15 @@ import retrofit2.converter.scalars.ScalarsConverterFactory
3727
import java.util.concurrent.TimeUnit
3828
import javax.inject.Singleton
3929

30+
/**
31+
* @brief Hilt module for providing network-related singleton components.
32+
*
33+
* This module is responsible for setting up and providing all dependencies required for
34+
* network operations throughout the application. It includes the configuration for Gson,
35+
* OkHttpClient, Retrofit, and the specific API service interfaces. All dependencies
36+
* provided here are scoped as singletons to ensure a single, shared instance is used
37+
* across the app.
38+
*/
4039
@Module
4140
@InstallIn(SingletonComponent::class)
4241
object NetworkModule {
@@ -160,4 +159,4 @@ object NetworkModule {
160159
@Provides
161160
@Singleton
162161
fun providesStudentLife(retrofit: Retrofit): StudentLife = retrofit.create(StudentLife::class.java)
163-
}
162+
}

PennMobile/src/main/java/com/pennapps/labs/pennmobile/dining/composables/DiningInsightsScreen.kt

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import android.util.Log
55
import androidx.compose.foundation.layout.Arrangement
66
import androidx.compose.foundation.layout.PaddingValues
77
import androidx.compose.foundation.layout.fillMaxSize
8+
import androidx.compose.foundation.layout.fillMaxWidth
89
import androidx.compose.foundation.layout.padding
910
import androidx.compose.foundation.lazy.LazyColumn
1011
import androidx.compose.foundation.lazy.items
@@ -19,6 +20,8 @@ import androidx.compose.ui.Modifier
1920
import androidx.compose.ui.unit.dp
2021
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
2122
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
23+
import com.pennapps.labs.pennmobile.compose.presentation.components.error.ErrorCard
24+
import com.pennapps.labs.pennmobile.compose.presentation.components.error.UserDisplayErrors
2225
import com.pennapps.labs.pennmobile.dining.composables.components.DiningBalancesCard
2326
import com.pennapps.labs.pennmobile.dining.composables.components.DiningPredictionCard
2427
import com.pennapps.labs.pennmobile.dining.viewmodels.DiningInsightsViewModel
@@ -54,6 +57,8 @@ fun DiningInsightsScreen(
5457
}
5558

5659
val cells by viewModel.cells.collectAsState()
60+
val pastBalances by viewModel.pastBalances.collectAsState()
61+
5762
LazyColumn(
5863
modifier =
5964
modifier
@@ -88,39 +93,64 @@ fun DiningInsightsScreen(
8893
)
8994
}
9095

91-
// Header for Dining Dollars Predictions
92-
item {
93-
Text(
94-
text = "Dining Dollars Predictions",
95-
fontFamily = GilroyExtraBold,
96-
color = MaterialTheme.colorScheme.onBackground,
97-
modifier = Modifier.padding(bottom = 4.dp),
98-
)
99-
}
96+
/*
97+
* When the pastBalances list size is:
98+
* a) 0 -> The user is either a junior/senior not on a dining plan or an RA who has a plan but for RAs, we never get the plan balances at all
99+
* b) 1 -> The user is a normal student on a plan. However, Campus Express currently has an issue
100+
* c) 2 or more -> Everything is alright. Render the balances graph.
101+
*/
102+
pastBalances?.diningBalancesList?.size?.let { balanceListSize ->
103+
if (balanceListSize == 0 || balanceListSize == 1) {
104+
item {
105+
ErrorCard(
106+
modifier =
107+
Modifier
108+
.padding(vertical = 12.dp)
109+
.fillMaxWidth(0.95f),
110+
errorMessage =
111+
when (balanceListSize) {
112+
0 -> UserDisplayErrors.PAST_BALANCES_NOT_AVAILABLE
113+
1 -> UserDisplayErrors.CAMPUS_EXPRESS_DOWN
114+
else -> "Error occurred"
115+
},
116+
)
117+
}
118+
} else {
119+
// Header for Dining Dollars Predictions
120+
item {
121+
Text(
122+
text = "Dining Dollars Predictions",
123+
fontFamily = GilroyExtraBold,
124+
color = MaterialTheme.colorScheme.onBackground,
125+
modifier = Modifier.padding(bottom = 4.dp),
126+
)
127+
}
100128

101-
// Dining Dollars Prediction cards
102-
items(cells.filter { it.type == "dining_dollars_predictions" }) { cell ->
103-
DiningPredictionCard(
104-
cell = cell,
105-
modifier = Modifier.padding(bottom = 12.dp),
106-
)
107-
}
129+
// Dining Dollars Prediction cards
130+
items(cells.filter { it.type == "dining_dollars_predictions" }) { cell ->
131+
DiningPredictionCard(
132+
cell = cell,
133+
modifier = Modifier.padding(bottom = 12.dp),
134+
)
135+
}
108136

109-
// Header for Swipes Predictions
110-
item {
111-
Text(
112-
text = "Swipes Predictions",
113-
fontFamily = GilroyExtraBold,
114-
color = MaterialTheme.colorScheme.onBackground,
115-
modifier = Modifier.padding(bottom = 4.dp),
116-
)
117-
}
137+
// Header for Swipes Predictions
138+
item {
139+
Text(
140+
text = "Swipes Predictions",
141+
fontFamily = GilroyExtraBold,
142+
color = MaterialTheme.colorScheme.onBackground,
143+
modifier = Modifier.padding(bottom = 4.dp),
144+
)
145+
}
118146

119-
// Swipes Prediction cards
120-
items(cells.filter { it.type == "dining_swipes_predictions" }) { cell ->
121-
DiningPredictionCard(
122-
cell = cell,
123-
)
147+
// Swipes Prediction cards
148+
items(cells.filter { it.type == "dining_swipes_predictions" }) { cell ->
149+
DiningPredictionCard(
150+
cell = cell,
151+
)
152+
}
153+
}
124154
}
125155
}
126156
}

PennMobile/src/main/java/com/pennapps/labs/pennmobile/dining/fragments/DiningFragment.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ import androidx.fragment.app.FragmentTransaction
5555
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
5656
import com.pennapps.labs.pennmobile.MainActivity
5757
import com.pennapps.labs.pennmobile.R
58-
import com.pennapps.labs.pennmobile.compose.presentation.components.AppSnackBar
58+
import com.pennapps.labs.pennmobile.compose.presentation.components.snackbar.AppSnackBar
5959
import com.pennapps.labs.pennmobile.compose.presentation.theme.AppColors
6060
import com.pennapps.labs.pennmobile.compose.presentation.theme.AppTheme
6161
import com.pennapps.labs.pennmobile.compose.presentation.theme.CustomTextStyles

0 commit comments

Comments
 (0)