Rebrand from Casera/MyCrib to honeyDue
Total rebrand across KMM project: - Kotlin package: com.example.casera -> com.tt.honeyDue (dirs + declarations) - Gradle: rootProject.name, namespace, applicationId - Android: manifest, strings.xml (all languages), widget resources - iOS: pbxproj bundle IDs, Info.plist, entitlements, xcconfig - iOS directories: Casera/ -> HoneyDue/, CaseraTests/ -> HoneyDueTests/, etc. - Swift source: all class/struct/enum renames - Deep links: casera:// -> honeydue://, .casera -> .honeydue - App icons replaced with honeyDue honeycomb icon - Domains: casera.treytartt.com -> honeyDue.treytartt.com - Bundle IDs: com.tt.casera -> com.tt.honeyDue - Database table names preserved Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
715
composeApp/src/commonMain/kotlin/com/tt/honeyDue/App.kt
Normal file
715
composeApp/src/commonMain/kotlin/com/tt/honeyDue/App.kt
Normal file
@@ -0,0 +1,715 @@
|
||||
package com.tt.honeyDue
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.safeContentPadding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tt.honeyDue.ui.screens.AddResidenceScreen
|
||||
import com.tt.honeyDue.ui.screens.EditResidenceScreen
|
||||
import com.tt.honeyDue.ui.screens.EditTaskScreen
|
||||
import com.tt.honeyDue.ui.screens.ForgotPasswordScreen
|
||||
import com.tt.honeyDue.ui.screens.HomeScreen
|
||||
import com.tt.honeyDue.ui.screens.LoginScreen
|
||||
import com.tt.honeyDue.ui.screens.RegisterScreen
|
||||
import com.tt.honeyDue.ui.screens.ResetPasswordScreen
|
||||
import com.tt.honeyDue.ui.screens.ResidenceDetailScreen
|
||||
import com.tt.honeyDue.ui.screens.ResidencesScreen
|
||||
import com.tt.honeyDue.ui.screens.TasksScreen
|
||||
import com.tt.honeyDue.ui.screens.VerifyEmailScreen
|
||||
import com.tt.honeyDue.ui.screens.VerifyResetCodeScreen
|
||||
import com.tt.honeyDue.ui.screens.onboarding.OnboardingScreen
|
||||
import com.tt.honeyDue.viewmodel.OnboardingViewModel
|
||||
import com.tt.honeyDue.viewmodel.PasswordResetViewModel
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import org.jetbrains.compose.resources.painterResource
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.toRoute
|
||||
import com.tt.honeyDue.ui.screens.MainScreen
|
||||
import com.tt.honeyDue.ui.screens.ManageUsersScreen
|
||||
import com.tt.honeyDue.ui.screens.NotificationPreferencesScreen
|
||||
import com.tt.honeyDue.ui.screens.ProfileScreen
|
||||
import com.tt.honeyDue.ui.theme.HoneyDueTheme
|
||||
import com.tt.honeyDue.ui.theme.ThemeManager
|
||||
import com.tt.honeyDue.navigation.*
|
||||
import com.tt.honeyDue.repository.LookupsRepository
|
||||
import com.tt.honeyDue.models.Residence
|
||||
import com.tt.honeyDue.models.TaskCategory
|
||||
import com.tt.honeyDue.models.TaskDetail
|
||||
import com.tt.honeyDue.models.TaskFrequency
|
||||
import com.tt.honeyDue.models.TaskPriority
|
||||
import com.tt.honeyDue.network.ApiResult
|
||||
import com.tt.honeyDue.network.AuthApi
|
||||
import com.tt.honeyDue.data.DataManager
|
||||
import com.tt.honeyDue.network.APILayer
|
||||
import com.tt.honeyDue.platform.ContractorImportHandler
|
||||
import com.tt.honeyDue.platform.PlatformUpgradeScreen
|
||||
import com.tt.honeyDue.platform.ResidenceImportHandler
|
||||
|
||||
import honeydue.composeapp.generated.resources.Res
|
||||
import honeydue.composeapp.generated.resources.compose_multiplatform
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun App(
|
||||
deepLinkResetToken: String? = null,
|
||||
onClearDeepLinkToken: () -> Unit = {},
|
||||
navigateToTaskId: Int? = null,
|
||||
onClearNavigateToTask: () -> Unit = {},
|
||||
pendingContractorImportUri: Any? = null,
|
||||
onClearContractorImport: () -> Unit = {},
|
||||
pendingResidenceImportUri: Any? = null,
|
||||
onClearResidenceImport: () -> Unit = {}
|
||||
) {
|
||||
var isLoggedIn by remember { mutableStateOf(DataManager.authToken.value != null) }
|
||||
var isVerified by remember { mutableStateOf(false) }
|
||||
var isCheckingAuth by remember { mutableStateOf(true) }
|
||||
var hasCompletedOnboarding by remember { mutableStateOf(DataManager.hasCompletedOnboarding.value) }
|
||||
val navController = rememberNavController()
|
||||
|
||||
// Handle navigation from notification tap
|
||||
// Note: The actual navigation to the task column happens in MainScreen -> AllTasksScreen
|
||||
// We just need to ensure the user is on MainRoute when a task navigation is requested
|
||||
LaunchedEffect(navigateToTaskId) {
|
||||
if (navigateToTaskId != null && isLoggedIn && isVerified) {
|
||||
// Ensure we're on the main screen - MainScreen will handle navigating to the tasks tab
|
||||
navController.navigate(MainRoute) {
|
||||
popUpTo(MainRoute) { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for stored token and verification status on app start
|
||||
LaunchedEffect(Unit) {
|
||||
val hasToken = DataManager.authToken.value != null
|
||||
isLoggedIn = hasToken
|
||||
|
||||
if (hasToken) {
|
||||
// Fetch current user to check verification status
|
||||
when (val result = APILayer.getCurrentUser(forceRefresh = true)) {
|
||||
is ApiResult.Success -> {
|
||||
isVerified = result.data.verified
|
||||
APILayer.initializeLookups()
|
||||
}
|
||||
else -> {
|
||||
// If fetching user fails, clear DataManager and logout
|
||||
DataManager.clear()
|
||||
isLoggedIn = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isCheckingAuth = false
|
||||
}
|
||||
|
||||
val currentTheme by remember { derivedStateOf { ThemeManager.currentTheme } }
|
||||
|
||||
HoneyDueTheme(themeColors = currentTheme) {
|
||||
// Handle contractor file imports (Android-specific, no-op on other platforms)
|
||||
ContractorImportHandler(
|
||||
pendingContractorImportUri = pendingContractorImportUri,
|
||||
onClearContractorImport = onClearContractorImport
|
||||
)
|
||||
|
||||
// Handle residence file imports (Android-specific, no-op on other platforms)
|
||||
ResidenceImportHandler(
|
||||
pendingResidenceImportUri = pendingResidenceImportUri,
|
||||
onClearResidenceImport = onClearResidenceImport
|
||||
)
|
||||
|
||||
if (isCheckingAuth) {
|
||||
// Show loading screen while checking auth
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
return@HoneyDueTheme
|
||||
}
|
||||
|
||||
val startDestination = when {
|
||||
deepLinkResetToken != null -> ForgotPasswordRoute
|
||||
!hasCompletedOnboarding -> OnboardingRoute
|
||||
!isLoggedIn -> LoginRoute
|
||||
!isVerified -> VerifyEmailRoute
|
||||
else -> MainRoute
|
||||
}
|
||||
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = startDestination
|
||||
) {
|
||||
composable<OnboardingRoute> {
|
||||
val onboardingViewModel: OnboardingViewModel = viewModel { OnboardingViewModel() }
|
||||
|
||||
OnboardingScreen(
|
||||
viewModel = onboardingViewModel,
|
||||
onComplete = {
|
||||
// Mark onboarding as complete
|
||||
DataManager.setHasCompletedOnboarding(true)
|
||||
hasCompletedOnboarding = true
|
||||
isLoggedIn = true
|
||||
isVerified = true
|
||||
// Note: Lookups are already initialized by APILayer during login/register
|
||||
|
||||
// Navigate to main screen
|
||||
navController.navigate(MainRoute) {
|
||||
popUpTo<OnboardingRoute> { inclusive = true }
|
||||
}
|
||||
},
|
||||
onLoginSuccess = { verified ->
|
||||
// User logged in through onboarding login dialog
|
||||
DataManager.setHasCompletedOnboarding(true)
|
||||
hasCompletedOnboarding = true
|
||||
isLoggedIn = true
|
||||
isVerified = verified
|
||||
// Note: Lookups are already initialized by APILayer.login()
|
||||
|
||||
if (verified) {
|
||||
navController.navigate(MainRoute) {
|
||||
popUpTo<OnboardingRoute> { inclusive = true }
|
||||
}
|
||||
} else {
|
||||
navController.navigate(VerifyEmailRoute) {
|
||||
popUpTo<OnboardingRoute> { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<LoginRoute> {
|
||||
LoginScreen(
|
||||
onLoginSuccess = { user ->
|
||||
isLoggedIn = true
|
||||
isVerified = user.verified
|
||||
// Note: Lookups are already initialized by APILayer.login()
|
||||
|
||||
// Check if user is verified
|
||||
if (user.verified) {
|
||||
navController.navigate(MainRoute) {
|
||||
popUpTo<LoginRoute> { inclusive = true }
|
||||
}
|
||||
} else {
|
||||
navController.navigate(VerifyEmailRoute) {
|
||||
popUpTo<LoginRoute> { inclusive = true }
|
||||
}
|
||||
}
|
||||
},
|
||||
onNavigateToRegister = {
|
||||
navController.navigate(RegisterRoute)
|
||||
},
|
||||
onNavigateToForgotPassword = {
|
||||
navController.navigate(ForgotPasswordRoute)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<RegisterRoute> {
|
||||
RegisterScreen(
|
||||
onRegisterSuccess = {
|
||||
isLoggedIn = true
|
||||
isVerified = false
|
||||
// Note: Lookups are already initialized by APILayer.register()
|
||||
navController.navigate(VerifyEmailRoute) {
|
||||
popUpTo<RegisterRoute> { inclusive = true }
|
||||
}
|
||||
},
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<ForgotPasswordRoute> { backStackEntry ->
|
||||
// Create shared ViewModel for all password reset screens
|
||||
val parentEntry = remember(backStackEntry) {
|
||||
navController.getBackStackEntry<ForgotPasswordRoute>()
|
||||
}
|
||||
val passwordResetViewModel: PasswordResetViewModel = viewModel(parentEntry) {
|
||||
PasswordResetViewModel(deepLinkToken = deepLinkResetToken)
|
||||
}
|
||||
|
||||
ForgotPasswordScreen(
|
||||
onNavigateBack = {
|
||||
// Clear deep link token when navigating back to login
|
||||
onClearDeepLinkToken()
|
||||
navController.popBackStack()
|
||||
},
|
||||
onNavigateToVerify = {
|
||||
navController.navigate(VerifyResetCodeRoute)
|
||||
},
|
||||
onNavigateToReset = {
|
||||
navController.navigate(ResetPasswordRoute)
|
||||
},
|
||||
viewModel = passwordResetViewModel
|
||||
)
|
||||
}
|
||||
|
||||
composable<VerifyResetCodeRoute> { backStackEntry ->
|
||||
// Use shared ViewModel from ForgotPasswordRoute
|
||||
val parentEntry = remember(backStackEntry) {
|
||||
navController.getBackStackEntry<ForgotPasswordRoute>()
|
||||
}
|
||||
val passwordResetViewModel: PasswordResetViewModel = viewModel(parentEntry) { PasswordResetViewModel() }
|
||||
|
||||
VerifyResetCodeScreen(
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onNavigateToReset = {
|
||||
navController.navigate(ResetPasswordRoute)
|
||||
},
|
||||
viewModel = passwordResetViewModel
|
||||
)
|
||||
}
|
||||
|
||||
composable<ResetPasswordRoute> { backStackEntry ->
|
||||
// Use shared ViewModel from ForgotPasswordRoute
|
||||
val parentEntry = remember(backStackEntry) {
|
||||
navController.getBackStackEntry<ForgotPasswordRoute>()
|
||||
}
|
||||
val passwordResetViewModel: PasswordResetViewModel = viewModel(parentEntry) { PasswordResetViewModel() }
|
||||
|
||||
// Set up auto-login callback
|
||||
LaunchedEffect(Unit) {
|
||||
passwordResetViewModel.onLoginSuccess = { verified ->
|
||||
isLoggedIn = true
|
||||
isVerified = verified
|
||||
onClearDeepLinkToken()
|
||||
// Navigate directly to main app or verification screen
|
||||
if (verified) {
|
||||
navController.navigate(MainRoute) {
|
||||
popUpTo<ForgotPasswordRoute> { inclusive = true }
|
||||
}
|
||||
} else {
|
||||
navController.navigate(VerifyEmailRoute) {
|
||||
popUpTo<ForgotPasswordRoute> { inclusive = true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ResetPasswordScreen(
|
||||
onPasswordResetSuccess = {
|
||||
// Fallback: manual return to login (only shown if auto-login fails)
|
||||
onClearDeepLinkToken()
|
||||
navController.navigate(LoginRoute) {
|
||||
popUpTo<ForgotPasswordRoute> { inclusive = true }
|
||||
}
|
||||
},
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
viewModel = passwordResetViewModel
|
||||
)
|
||||
}
|
||||
|
||||
composable<VerifyEmailRoute> {
|
||||
VerifyEmailScreen(
|
||||
onVerifySuccess = {
|
||||
isVerified = true
|
||||
navController.navigate(MainRoute) {
|
||||
popUpTo<VerifyEmailRoute> { inclusive = true }
|
||||
}
|
||||
},
|
||||
onLogout = {
|
||||
// Clear token and lookups on logout
|
||||
DataManager.clear()
|
||||
isLoggedIn = false
|
||||
isVerified = false
|
||||
navController.navigate(LoginRoute) {
|
||||
popUpTo<VerifyEmailRoute> { inclusive = true }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<MainRoute> {
|
||||
MainScreen(
|
||||
onLogout = {
|
||||
// Clear token and lookups on logout
|
||||
DataManager.clear()
|
||||
isLoggedIn = false
|
||||
isVerified = false
|
||||
navController.navigate(LoginRoute) {
|
||||
popUpTo<MainRoute> { inclusive = true }
|
||||
}
|
||||
},
|
||||
onResidenceClick = { residenceId ->
|
||||
navController.navigate(ResidenceDetailRoute(residenceId))
|
||||
},
|
||||
onAddResidence = {
|
||||
navController.navigate(AddResidenceRoute)
|
||||
},
|
||||
onAddTask = {
|
||||
// Tasks are added from within a residence
|
||||
// Navigate to first residence or show message if no residences exist
|
||||
// For now, this will be handled by the UI showing "add a property first"
|
||||
},
|
||||
navigateToTaskId = navigateToTaskId,
|
||||
onClearNavigateToTask = onClearNavigateToTask,
|
||||
onNavigateToEditResidence = { residence ->
|
||||
navController.navigate(
|
||||
EditResidenceRoute(
|
||||
residenceId = residence.id,
|
||||
name = residence.name,
|
||||
propertyType = residence.propertyTypeId,
|
||||
streetAddress = residence.streetAddress,
|
||||
apartmentUnit = residence.apartmentUnit,
|
||||
city = residence.city,
|
||||
stateProvince = residence.stateProvince,
|
||||
postalCode = residence.postalCode,
|
||||
country = residence.country,
|
||||
bedrooms = residence.bedrooms,
|
||||
bathrooms = residence.bathrooms?.toFloat(),
|
||||
squareFootage = residence.squareFootage,
|
||||
lotSize = residence.lotSize?.toFloat(),
|
||||
yearBuilt = residence.yearBuilt,
|
||||
description = residence.description,
|
||||
isPrimary = residence.isPrimary,
|
||||
ownerUserName = residence.ownerUsername,
|
||||
createdAt = residence.createdAt,
|
||||
updatedAt = residence.updatedAt,
|
||||
owner = residence.ownerId
|
||||
)
|
||||
)
|
||||
},
|
||||
onNavigateToEditTask = { task ->
|
||||
navController.navigate(
|
||||
EditTaskRoute(
|
||||
taskId = task.id,
|
||||
residenceId = task.residenceId,
|
||||
title = task.title,
|
||||
description = task.description,
|
||||
categoryId = task.category?.id ?: 0,
|
||||
categoryName = task.category?.name ?: "",
|
||||
frequencyId = task.frequency?.id ?: 0,
|
||||
frequencyName = task.frequency?.name ?: "",
|
||||
priorityId = task.priority?.id ?: 0,
|
||||
priorityName = task.priority?.name ?: "",
|
||||
inProgress = task.inProgress,
|
||||
dueDate = task.dueDate,
|
||||
estimatedCost = task.estimatedCost?.toString(),
|
||||
createdAt = task.createdAt,
|
||||
updatedAt = task.updatedAt
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<HomeRoute> {
|
||||
HomeScreen(
|
||||
onNavigateToResidences = {
|
||||
navController.navigate(MainRoute)
|
||||
},
|
||||
onNavigateToTasks = {
|
||||
navController.navigate(TasksRoute)
|
||||
},
|
||||
onLogout = {
|
||||
// Clear token and lookups on logout
|
||||
DataManager.clear()
|
||||
isLoggedIn = false
|
||||
isVerified = false
|
||||
navController.navigate(LoginRoute) {
|
||||
popUpTo<HomeRoute> { inclusive = true }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<ResidencesRoute> { backStackEntry ->
|
||||
// Get refresh flag from saved state (set when returning from add/edit)
|
||||
val shouldRefresh = backStackEntry.savedStateHandle.get<Boolean>("refresh") ?: false
|
||||
|
||||
ResidencesScreen(
|
||||
onResidenceClick = { residenceId ->
|
||||
navController.navigate(ResidenceDetailRoute(residenceId))
|
||||
},
|
||||
onAddResidence = {
|
||||
navController.navigate(AddResidenceRoute)
|
||||
},
|
||||
onNavigateToProfile = {
|
||||
navController.navigate(ProfileRoute)
|
||||
},
|
||||
shouldRefresh = shouldRefresh,
|
||||
onLogout = {
|
||||
// Clear token and lookups on logout
|
||||
DataManager.clear()
|
||||
isLoggedIn = false
|
||||
isVerified = false
|
||||
navController.navigate(LoginRoute) {
|
||||
popUpTo<HomeRoute> { inclusive = true }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<AddResidenceRoute> {
|
||||
AddResidenceScreen(
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onResidenceCreated = {
|
||||
// Set refresh flag before navigating back
|
||||
navController.previousBackStackEntry?.savedStateHandle?.set("refresh", true)
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<EditResidenceRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<EditResidenceRoute>()
|
||||
EditResidenceScreen(
|
||||
residence = Residence(
|
||||
id = route.residenceId,
|
||||
ownerId = route.owner ?: 0,
|
||||
name = route.name,
|
||||
propertyTypeId = route.propertyType,
|
||||
streetAddress = route.streetAddress ?: "",
|
||||
apartmentUnit = route.apartmentUnit ?: "",
|
||||
city = route.city ?: "",
|
||||
stateProvince = route.stateProvince ?: "",
|
||||
postalCode = route.postalCode ?: "",
|
||||
country = route.country ?: "",
|
||||
bedrooms = route.bedrooms,
|
||||
bathrooms = route.bathrooms?.toDouble(),
|
||||
squareFootage = route.squareFootage,
|
||||
lotSize = route.lotSize?.toDouble(),
|
||||
yearBuilt = route.yearBuilt,
|
||||
description = route.description ?: "",
|
||||
purchaseDate = null,
|
||||
purchasePrice = null,
|
||||
isPrimary = route.isPrimary,
|
||||
createdAt = route.createdAt,
|
||||
updatedAt = route.updatedAt
|
||||
),
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onResidenceUpdated = {
|
||||
// Set refresh flag before navigating back
|
||||
navController.previousBackStackEntry?.savedStateHandle?.set("refresh", true)
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<TasksRoute> {
|
||||
TasksScreen(
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<ResidenceDetailRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<ResidenceDetailRoute>()
|
||||
ResidenceDetailScreen(
|
||||
residenceId = route.residenceId,
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onNavigateToEditResidence = { residence ->
|
||||
navController.navigate(
|
||||
EditResidenceRoute(
|
||||
residenceId = residence.id,
|
||||
name = residence.name,
|
||||
propertyType = residence.propertyTypeId,
|
||||
streetAddress = residence.streetAddress,
|
||||
apartmentUnit = residence.apartmentUnit,
|
||||
city = residence.city,
|
||||
stateProvince = residence.stateProvince,
|
||||
postalCode = residence.postalCode,
|
||||
country = residence.country,
|
||||
bedrooms = residence.bedrooms,
|
||||
bathrooms = residence.bathrooms?.toFloat(),
|
||||
squareFootage = residence.squareFootage,
|
||||
lotSize = residence.lotSize?.toFloat(),
|
||||
yearBuilt = residence.yearBuilt,
|
||||
description = residence.description,
|
||||
isPrimary = residence.isPrimary,
|
||||
ownerUserName = residence.ownerUsername,
|
||||
createdAt = residence.createdAt,
|
||||
updatedAt = residence.updatedAt,
|
||||
owner = residence.ownerId
|
||||
)
|
||||
)
|
||||
},
|
||||
onNavigateToEditTask = { task ->
|
||||
navController.navigate(
|
||||
EditTaskRoute(
|
||||
taskId = task.id,
|
||||
residenceId = task.residenceId,
|
||||
title = task.title,
|
||||
description = task.description,
|
||||
categoryId = task.category?.id ?: 0,
|
||||
categoryName = task.category?.name ?: "",
|
||||
frequencyId = task.frequency?.id ?: 0,
|
||||
frequencyName = task.frequency?.name ?: "",
|
||||
priorityId = task.priority?.id ?: 0,
|
||||
priorityName = task.priority?.name ?: "",
|
||||
inProgress = task.inProgress,
|
||||
dueDate = task.dueDate,
|
||||
estimatedCost = task.estimatedCost?.toString(),
|
||||
createdAt = task.createdAt,
|
||||
updatedAt = task.updatedAt
|
||||
)
|
||||
)
|
||||
},
|
||||
onNavigateToContractorDetail = { contractorId ->
|
||||
navController.navigate(ContractorDetailRoute(contractorId))
|
||||
},
|
||||
onNavigateToManageUsers = { residenceId, residenceName, isPrimaryOwner, ownerId ->
|
||||
navController.navigate(
|
||||
ManageUsersRoute(
|
||||
residenceId = residenceId,
|
||||
residenceName = residenceName,
|
||||
isPrimaryOwner = isPrimaryOwner,
|
||||
residenceOwnerId = ownerId
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<ManageUsersRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<ManageUsersRoute>()
|
||||
ManageUsersScreen(
|
||||
residenceId = route.residenceId,
|
||||
residenceName = route.residenceName,
|
||||
isPrimaryOwner = route.isPrimaryOwner,
|
||||
residenceOwnerId = route.residenceOwnerId,
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<UpgradeRoute> {
|
||||
PlatformUpgradeScreen(
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onSubscriptionChanged = {
|
||||
// Subscription state updated via DataManager
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<EditTaskRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<EditTaskRoute>()
|
||||
EditTaskScreen(
|
||||
task = TaskDetail(
|
||||
id = route.taskId,
|
||||
residenceId = route.residenceId,
|
||||
createdById = 0,
|
||||
title = route.title,
|
||||
description = route.description ?: "",
|
||||
category = TaskCategory(id = route.categoryId, name = route.categoryName),
|
||||
frequency = TaskFrequency(
|
||||
id = route.frequencyId,
|
||||
name = route.frequencyName,
|
||||
days = null
|
||||
),
|
||||
priority = TaskPriority(id = route.priorityId, name = route.priorityName),
|
||||
inProgress = route.inProgress,
|
||||
dueDate = route.dueDate,
|
||||
estimatedCost = route.estimatedCost?.toDoubleOrNull(),
|
||||
createdAt = route.createdAt,
|
||||
updatedAt = route.updatedAt,
|
||||
completions = emptyList()
|
||||
),
|
||||
onNavigateBack = { navController.popBackStack() },
|
||||
onTaskUpdated = { navController.popBackStack() }
|
||||
)
|
||||
}
|
||||
|
||||
composable<ProfileRoute> {
|
||||
ProfileScreen(
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onLogout = {
|
||||
// Clear token and lookups on logout
|
||||
DataManager.clear()
|
||||
isLoggedIn = false
|
||||
isVerified = false
|
||||
navController.navigate(LoginRoute) {
|
||||
popUpTo<ProfileRoute> { inclusive = true }
|
||||
}
|
||||
},
|
||||
onNavigateToNotificationPreferences = {
|
||||
navController.navigate(NotificationPreferencesRoute)
|
||||
},
|
||||
onNavigateToUpgrade = {
|
||||
navController.navigate(UpgradeRoute)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<NotificationPreferencesRoute> {
|
||||
NotificationPreferencesScreen(
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
MaterialTheme {
|
||||
var showContent by remember { mutableStateOf(false) }
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.primaryContainer)
|
||||
.safeContentPadding()
|
||||
.fillMaxSize(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Button(onClick = { showContent = !showContent }) {
|
||||
Text("Click me!")
|
||||
}
|
||||
AnimatedVisibility(showContent) {
|
||||
val greeting = remember { Greeting().greet() }
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Image(painterResource(Res.drawable.compose_multiplatform), null)
|
||||
Text("Compose: $greeting")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
Reference in New Issue
Block a user