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

Implement View Model for Payment Due Date #46

Merged
merged 8 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -11,13 +11,13 @@ import edu.card.clarity.presentation.addCardScreen.AddCardScreen
import edu.card.clarity.presentation.homeScreen.HomeScreen
import edu.card.clarity.presentation.myCardScreen.MyCardsScreen
import edu.card.clarity.presentation.PurchaseScreen
import edu.card.clarity.presentation.UpcomingPaymentsScreen
import edu.card.clarity.presentation.addBenefitScreen.AddBenefitScreen
import edu.card.clarity.presentation.addCardScreen.AddCardScreen
import edu.card.clarity.presentation.myBenefitsScreen.MyBenefitsScreen
import edu.card.clarity.presentation.myCardScreen.MyCardsScreen
import edu.card.clarity.presentation.utils.ArgumentNames
import edu.card.clarity.presentation.utils.Destinations
import edu.card.clarity.presentation.upcomingPaymentsScreen.UpcomingPaymentsScreen

@Composable
fun BottomNavGraph(navController: NavHostController) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package edu.card.clarity.presentation.upcomingPaymentsScreen

import androidx.compose.ui.graphics.Color
import android.icu.util.Calendar

data class PaymentDueDateUiState(
val cardName: String,
val dueDate: Calendar,
val backgroundColor: Color
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package edu.card.clarity.presentation.upcomingPaymentsScreen

import android.icu.util.Calendar
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import edu.card.clarity.domain.creditCard.CreditCardInfo
import edu.card.clarity.enums.CardNetworkType
import edu.card.clarity.presentation.utils.WhileUiSubscribed
import edu.card.clarity.repositories.creditCard.CashBackCreditCardRepository
import edu.card.clarity.repositories.creditCard.PointBackCreditCardRepository
import kotlinx.coroutines.flow.*
import java.time.Instant
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import javax.inject.Inject

@HiltViewModel
class PaymentDueDateViewModel @Inject constructor(
private val cashBackCreditCardRepository: CashBackCreditCardRepository,
private val pointBackCreditCardRepository: PointBackCreditCardRepository
) : ViewModel() {

private val _filteredCreditCards = combine(
cashBackCreditCardRepository.getAllCreditCardInfoStream(),
pointBackCreditCardRepository.getAllCreditCardInfoStream()
) { cashBack, pointBack ->
cashBack + pointBack
}

val uiState: StateFlow<List<PaymentDueDateUiState>> = _filteredCreditCards
.map { cardList -> cardList.map { it.toUiState() } }
.stateIn(
scope = viewModelScope,
started = WhileUiSubscribed,
initialValue = listOf()
)

fun getNextPayment(): PaymentDueDateUiState? {
val sortedPayments = uiState.value.sortedBy { parseDate(it.dueDate) }
return sortedPayments.firstOrNull { parseDate(it.dueDate).isAfter(LocalDate.now()) }
}

private fun parseDate(calendar: Calendar): LocalDate {
val instant = calendar.toInstant()
val zoneId = ZoneId.systemDefault()
return instant.atZone(zoneId).toLocalDate()
}

companion object {
private val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")

private fun CreditCardInfo.toUiState() = PaymentDueDateUiState(
cardName = name,
dueDate = paymentDueDate,
backgroundColor = when (cardNetworkType) {
CardNetworkType.Visa -> Color.Green
CardNetworkType.MasterCard -> Color.Red
CardNetworkType.AMEX -> Color.Blue
}
)

private fun Calendar.toInstant(): Instant {
return this.time.toInstant()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package edu.card.clarity.presentation
package edu.card.clarity.presentation.upcomingPaymentsScreen

import android.icu.util.Calendar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
Expand All @@ -9,33 +10,24 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material3.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.tooling.preview.Preview
import edu.card.clarity.presentation.myCardScreen.CardInfo
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import edu.card.clarity.ui.theme.CardClarityTheme
import edu.card.clarity.ui.theme.CardClarityTypography
import java.time.LocalDate
import java.time.YearMonth


val cards = listOf(
CardInfo(cardName = "TD Aeroplan Visa Infinite Card", dueDate = "2024-06-29", backgroundColor = Color(0xFFAED8FF)),
CardInfo(cardName = "American Express Platinum Card", dueDate = "2024-06-13", backgroundColor = Color(0xFFB7FF9E)),
CardInfo(cardName = "CIBC Dividend", dueDate = "2024-06-03", backgroundColor = Color(0xFFFF9EB8)),
)

@Composable
fun UpcomingPaymentsScreen() {
fun UpcomingPaymentsScreen(viewModel: PaymentDueDateViewModel = hiltViewModel()) {
val cards by viewModel.uiState.collectAsState()

CardClarityTheme {
Column(
modifier = Modifier
Expand All @@ -47,6 +39,7 @@ fun UpcomingPaymentsScreen() {
) {
Header()
CalendarPager(cards)
PaymentLegend(cards)
UpcomingPayment(cards)
}
}
Expand All @@ -65,7 +58,7 @@ fun Header() {
}

@Composable
fun CalendarPager(cards: List<CardInfo>) {
fun CalendarPager(cards: List<PaymentDueDateUiState>) {
var currentMonth by remember { mutableStateOf(YearMonth.now()) }

Column {
Expand All @@ -80,7 +73,7 @@ fun CalendarPager(cards: List<CardInfo>) {
@Composable
fun MonthView(
month: YearMonth,
cards: List<CardInfo>,
cards: List<PaymentDueDateUiState>,
onMonthChange: (YearMonth) -> Unit
) {
val daysInMonth: Int = month.lengthOfMonth()
Expand Down Expand Up @@ -128,6 +121,7 @@ fun MonthView(
)
}

// Day cells with payment coloring
items(daysInMonth + dayOfWeekOffset) { index: Int ->
if (index >= dayOfWeekOffset) {
val day: Int = index - dayOfWeekOffset + 1
Expand All @@ -138,15 +132,17 @@ fun MonthView(
}
}
}
PaymentLegend(cards = cards)
}

@Composable
fun DayCell(day: Int, month: YearMonth, cards: List<CardInfo>) {
fun DayCell(day: Int, month: YearMonth, cards: List<PaymentDueDateUiState>) {
val date: LocalDate = month.atDay(day)
// check if any card has a due date with the same day of the month as 'day'
val paymentColor = cards.find { card ->
val cardDueDate = LocalDate.parse(card.dueDate)
val cardDueDate = LocalDate.of(
card.dueDate.get(Calendar.YEAR),
card.dueDate.get(Calendar.MONTH) + 1, // Calendar.MONTH is zero-based in java.util.Calendar
card.dueDate.get(Calendar.DAY_OF_MONTH)
)
cardDueDate.dayOfMonth == day
}?.backgroundColor ?: Color.LightGray

Expand All @@ -164,8 +160,9 @@ fun DayCell(day: Int, month: YearMonth, cards: List<CardInfo>) {
}
}


@Composable
fun PaymentLegend(cards: List<CardInfo>) {
fun PaymentLegend(cards: List<PaymentDueDateUiState>) {
Column(
modifier = Modifier
.padding(vertical = 8.dp)
Expand Down Expand Up @@ -193,14 +190,19 @@ fun PaymentLegend(cards: List<CardInfo>) {
}
}



@Composable
fun UpcomingPayment(cards: List<CardInfo>) {
val sortedPayments = cards.sortedBy { LocalDate.parse(it.dueDate) }
val nextPayment = sortedPayments.firstOrNull { LocalDate.parse(it.dueDate).isAfter(LocalDate.now()) }
fun UpcomingPayment(cards: List<PaymentDueDateUiState>) {
val sortedPayments = cards.sortedBy { card ->
LocalDate.of(
card.dueDate.get(Calendar.YEAR),
card.dueDate.get(Calendar.MONTH) + 1, // Calendar.MONTH is zero-based
card.dueDate.get(Calendar.DAY_OF_MONTH)
)
}
val now = LocalDate.now()
val nextPayment = sortedPayments.firstOrNull { it.dueDate.toLocalDate().isAfter(now) }

Column (
Column(
modifier = Modifier
.padding(vertical = 8.dp)
.fillMaxWidth(),
Expand All @@ -209,8 +211,7 @@ fun UpcomingPayment(cards: List<CardInfo>) {
Text(
text = "Next payment",
style = CardClarityTypography.titleLarge,
modifier = Modifier
.padding(vertical = 8.dp)
modifier = Modifier.padding(vertical = 8.dp)
)
if (nextPayment != null) {
Box(
Expand All @@ -224,8 +225,13 @@ fun UpcomingPayment(cards: List<CardInfo>) {
.padding(all = 16.dp),
horizontalArrangement = Arrangement.Start
) {
val dueDate = LocalDate.of(
nextPayment.dueDate.get(Calendar.YEAR),
nextPayment.dueDate.get(Calendar.MONTH) + 1,
nextPayment.dueDate.get(Calendar.DAY_OF_MONTH)
)
Text(
text = "${nextPayment.cardName}\nDue Date: ${nextPayment.dueDate}",
text = "${nextPayment.cardName}\nDue Date: $dueDate",
style = CardClarityTypography.bodyLarge
)
}
Expand All @@ -240,6 +246,14 @@ fun UpcomingPayment(cards: List<CardInfo>) {
}
}

private fun Calendar.toLocalDate(): LocalDate {
return LocalDate.of(
this.get(Calendar.YEAR),
this.get(Calendar.MONTH) + 1, // Adjust for zero-based month index
this.get(Calendar.DAY_OF_MONTH)
)
}


@Preview(showBackground = true)
@Composable
Expand Down