Modernize Android UI design system to match iOS

- Create comprehensive design system with Theme.kt, Type.kt, and Shape.kt
- Update color palette to match iOS (primary #2563EB, accent #8B5CF6)
- Implement modern typography scale across all text styles
- Add consistent corner radius system (AppRadius)

UI Improvements:
- HomeScreen: Add personalized greeting, gradient icons, modern stats
- TaskCard: Pill-style badges, semantic colors, clean metadata
- ResidencesScreen: Gradient circular icons, location indicators
- LoginScreen: Gradient button background, modern card styling

Design Features:
- Gradient backgrounds for visual interest
- Semantic color coding for states
- Flat design with minimal elevation
- Consistent spacing and typography
- Material3 shapes throughout

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-10 12:12:30 -06:00
parent e13448083c
commit 764a90cb41
7 changed files with 633 additions and 238 deletions

View File

@@ -1,6 +1,8 @@
package com.mycrib.android.ui.components.task package com.mycrib.android.ui.components.task
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
@@ -8,8 +10,11 @@ import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.mycrib.android.ui.theme.AppColors
import com.mycrib.shared.models.TaskDetail import com.mycrib.shared.models.TaskDetail
import com.mycrib.shared.models.TaskCategory import com.mycrib.shared.models.TaskCategory
import com.mycrib.shared.models.TaskPriority import com.mycrib.shared.models.TaskPriority
@@ -32,10 +37,10 @@ fun TaskCard(
) { ) {
Card( Card(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp), shape = MaterialTheme.shapes.large,
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp), elevation = CardDefaults.cardElevation(defaultElevation = 0.dp),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceContainerLowest containerColor = MaterialTheme.colorScheme.surface
) )
) { ) {
Column( Column(
@@ -51,54 +56,75 @@ fun TaskCard(
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( Text(
text = task.title, text = task.title,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold color = MaterialTheme.colorScheme.onSurface
) )
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(8.dp))
// Pill-style category badge
Surface( Surface(
color = MaterialTheme.colorScheme.secondaryContainer, color = MaterialTheme.colorScheme.surfaceVariant,
shape = RoundedCornerShape(8.dp) shape = RoundedCornerShape(12.dp)
) { ) {
Text( Text(
text = task.category.name, text = task.category.name.uppercase(),
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSecondaryContainer color = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
} }
Column( Column(
horizontalAlignment = Alignment.End, horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(6.dp) verticalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
Surface( // Priority badge with semantic colors
color = when (task.priority.name.lowercase()) { val priorityColor = when (task.priority.name.lowercase()) {
"urgent" -> MaterialTheme.colorScheme.error "urgent", "high" -> Color(0xFFEF4444) // Error/Red
"high" -> MaterialTheme.colorScheme.errorContainer "medium" -> Color(0xFFF59E0B) // Warning/Orange
"medium" -> MaterialTheme.colorScheme.primaryContainer else -> Color(0xFF10B981) // Success/Green
else -> MaterialTheme.colorScheme.surfaceVariant }
}, Row(
shape = RoundedCornerShape(10.dp) modifier = Modifier
.background(
priorityColor.copy(alpha = 0.15f),
RoundedCornerShape(12.dp)
)
.padding(horizontal = 12.dp, vertical = 6.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) { ) {
Box(
modifier = Modifier
.size(6.dp)
.clip(CircleShape)
.background(priorityColor)
)
Text( Text(
text = task.priority.name.uppercase(), text = task.priority.name.uppercase(),
modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp), style = MaterialTheme.typography.labelSmall,
style = MaterialTheme.typography.labelMedium, color = priorityColor
fontWeight = FontWeight.Bold
) )
} }
// Status badge with semantic colors
if (task.status != null) { if (task.status != null) {
val statusColor = when (task.status.name.lowercase()) {
"completed" -> AppColors.taskCompleted
"in_progress" -> AppColors.taskInProgress
"pending" -> Color(0xFFF59E0B)
"cancelled" -> AppColors.taskCanceled
else -> Color(0xFF9CA3AF)
}
Surface( Surface(
color = MaterialTheme.colorScheme.tertiaryContainer, color = statusColor.copy(alpha = 0.15f),
shape = RoundedCornerShape(10.dp) shape = RoundedCornerShape(12.dp)
) { ) {
Text( Text(
text = task.status.name.uppercase(), text = task.status.name.replace("_", " ").uppercase(),
modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp), modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp),
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onTertiaryContainer color = statusColor
) )
} }
} }
@@ -114,42 +140,61 @@ fun TaskCard(
) )
} }
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider() HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(16.dp))
// Metadata pills
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
// Date pill
Row(
modifier = Modifier
.background(
MaterialTheme.colorScheme.surfaceVariant,
RoundedCornerShape(12.dp)
)
.padding(horizontal = 12.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp)
) { ) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon( Icon(
Icons.Default.CalendarToday, Icons.Default.CalendarToday,
contentDescription = null, contentDescription = null,
modifier = Modifier.size(16.dp), modifier = Modifier.size(14.dp),
tint = MaterialTheme.colorScheme.primary tint = MaterialTheme.colorScheme.onSurfaceVariant
) )
Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = task.nextScheduledDate ?: task.dueDate ?: "N/A", text = task.nextScheduledDate ?: task.dueDate ?: "N/A",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.labelSmall,
fontWeight = FontWeight.SemiBold color = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
// Cost pill
task.estimatedCost?.let { task.estimatedCost?.let {
Row(verticalAlignment = Alignment.CenterVertically) { Row(
modifier = Modifier
.background(
MaterialTheme.colorScheme.surfaceVariant,
RoundedCornerShape(12.dp)
)
.padding(horizontal = 12.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
Icon( Icon(
Icons.Default.AttachMoney, Icons.Default.AttachMoney,
contentDescription = null, contentDescription = null,
modifier = Modifier.size(16.dp), modifier = Modifier.size(14.dp),
tint = MaterialTheme.colorScheme.tertiary tint = MaterialTheme.colorScheme.onSurfaceVariant
) )
Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = "$$it", text = "$$it",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.labelSmall,
fontWeight = FontWeight.SemiBold, color = MaterialTheme.colorScheme.onSurfaceVariant
color = MaterialTheme.colorScheme.tertiary
) )
} }
} }
@@ -282,7 +327,8 @@ fun CompletionCard(completion: TaskCompletion) {
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant containerColor = MaterialTheme.colorScheme.surfaceVariant
), ),
shape = RoundedCornerShape(12.dp) shape = MaterialTheme.shapes.medium,
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier

View File

@@ -1,15 +1,22 @@
package com.mycrib.android.ui.screens package com.mycrib.android.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.mycrib.android.ui.theme.AppColors
import com.mycrib.android.ui.theme.AppRadius
import com.mycrib.android.viewmodel.ResidenceViewModel import com.mycrib.android.viewmodel.ResidenceViewModel
import com.mycrib.shared.network.ApiResult import com.mycrib.shared.network.ApiResult
@@ -30,10 +37,17 @@ fun HomeScreen(
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text("myCrib") }, title = { Text("myCrib", style = MaterialTheme.typography.headlineSmall) },
colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.background
),
actions = { actions = {
IconButton(onClick = onLogout) { IconButton(onClick = onLogout) {
Icon(Icons.Default.ExitToApp, contentDescription = "Logout") Icon(
Icons.Default.ExitToApp,
contentDescription = "Logout",
tint = MaterialTheme.colorScheme.primary
)
} }
} }
) )
@@ -42,71 +56,116 @@ fun HomeScreen(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.padding(paddingValues) .padding(paddingValues)
.padding(16.dp), .padding(horizontal = 16.dp, vertical = 12.dp),
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(20.dp)
) { ) {
// Personalized Greeting
Column(
modifier = Modifier.padding(vertical = 8.dp)
) {
Text(
text = "Welcome back",
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = "Manage your properties",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
// Summary Card // Summary Card
when (summaryState) { when (summaryState) {
is ApiResult.Success -> { is ApiResult.Success -> {
val summary = (summaryState as ApiResult.Success).data val summary = (summaryState as ApiResult.Success).data
Card( Card(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = MaterialTheme.shapes.large,
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.primaryContainer containerColor = MaterialTheme.colorScheme.surface
) ),
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(16.dp) .padding(20.dp)
) { ) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
// Gradient circular icon
Box(
modifier = Modifier
.size(44.dp)
.clip(CircleShape)
.background(
Brush.linearGradient(
listOf(
Color(0xFF2563EB),
Color(0xFF8B5CF6)
)
)
),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Home,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(24.dp)
)
}
Column {
Text( Text(
text = "Overview", text = "Overview",
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer color = MaterialTheme.colorScheme.onSurface
) )
Spacer(modifier = Modifier.height(8.dp)) Text(
text = "Your property stats",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
Spacer(modifier = Modifier.height(20.dp))
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly horizontalArrangement = Arrangement.SpaceEvenly
) { ) {
Column(horizontalAlignment = Alignment.CenterHorizontally) { StatItem(
Text( value = "${summary.residences.size}",
text = "${summary.residences.size}", label = "Properties",
style = MaterialTheme.typography.headlineMedium, color = Color(0xFF3B82F6)
color = MaterialTheme.colorScheme.onPrimaryContainer
) )
Text( Divider(
text = "Properties", modifier = Modifier
style = MaterialTheme.typography.bodySmall, .height(48.dp)
color = MaterialTheme.colorScheme.onPrimaryContainer .width(1.dp),
color = MaterialTheme.colorScheme.outlineVariant
) )
} StatItem(
Column(horizontalAlignment = Alignment.CenterHorizontally) { value = "${summary.summary.totalTasks}",
Text( label = "Total Tasks",
text = "${summary.summary.totalTasks}", color = Color(0xFF8B5CF6)
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer
) )
Text( Divider(
text = "Total Tasks", modifier = Modifier
style = MaterialTheme.typography.bodySmall, .height(48.dp)
color = MaterialTheme.colorScheme.onPrimaryContainer .width(1.dp),
color = MaterialTheme.colorScheme.outlineVariant
) )
} StatItem(
Column(horizontalAlignment = Alignment.CenterHorizontally) { value = "${summary.summary.totalPending}",
Text( label = "Pending",
text = "${summary.summary.totalPending}", color = Color(0xFFF59E0B)
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onPrimaryContainer
) )
Text(
text = "Pending",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onPrimaryContainer
)
}
} }
} }
} }
@@ -131,66 +190,126 @@ fun HomeScreen(
} }
// Residences Card // Residences Card
Card( NavigationCard(
modifier = Modifier title = "Properties",
.fillMaxWidth() subtitle = "Manage your residences",
.clickable { onNavigateToResidences() } icon = Icons.Default.Home,
) { iconColor = Color(0xFF3B82F6),
Row( onClick = onNavigateToResidences
modifier = Modifier
.fillMaxWidth()
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.Home,
contentDescription = null,
modifier = Modifier.size(48.dp)
) )
Spacer(modifier = Modifier.width(16.dp))
Column { // Tasks Card
Text( NavigationCard(
text = "Residences", title = "Tasks",
style = MaterialTheme.typography.titleLarge subtitle = "View and manage tasks",
) icon = Icons.Default.CheckCircle,
Text( iconColor = Color(0xFF10B981),
text = "Manage your properties", onClick = onNavigateToTasks
style = MaterialTheme.typography.bodyMedium
) )
} }
} }
} }
// Tasks Card @Composable
private fun StatItem(
value: String,
label: String,
color: Color
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Box(
modifier = Modifier
.size(36.dp)
.clip(CircleShape)
.background(color.copy(alpha = 0.1f)),
contentAlignment = Alignment.Center
) {
Text(
text = value,
style = MaterialTheme.typography.titleLarge,
color = color
)
}
Text(
text = label,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
@Composable
private fun NavigationCard(
title: String,
subtitle: String,
icon: androidx.compose.ui.graphics.vector.ImageVector,
iconColor: Color,
onClick: () -> Unit
) {
Card( Card(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { onNavigateToTasks() } .clickable { onClick() },
shape = MaterialTheme.shapes.large,
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
),
elevation = CardDefaults.cardElevation(defaultElevation = 0.dp)
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(24.dp), .padding(20.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
// Gradient circular icon
Box(
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
.background(
Brush.linearGradient(
listOf(
iconColor,
iconColor.copy(alpha = 0.7f)
)
)
),
contentAlignment = Alignment.Center
) { ) {
Icon( Icon(
Icons.Default.CheckCircle, icon,
contentDescription = null, contentDescription = null,
modifier = Modifier.size(48.dp) tint = Color.White,
modifier = Modifier.size(28.dp)
) )
Spacer(modifier = Modifier.width(16.dp)) }
Column {
Column(
modifier = Modifier.weight(1f)
) {
Text( Text(
text = "Tasks", text = title,
style = MaterialTheme.typography.titleLarge style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.onSurface
) )
Spacer(modifier = Modifier.height(4.dp))
Text( Text(
text = "View and manage tasks", text = subtitle,
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Icon(
Icons.Default.KeyboardArrowRight,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
} }
} }
}
}
}

View File

@@ -2,6 +2,7 @@ package com.mycrib.android.ui.screens
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
@@ -11,6 +12,8 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
@@ -63,11 +66,11 @@ fun LoginScreen(
modifier = Modifier modifier = Modifier
.fillMaxWidth(0.9f) .fillMaxWidth(0.9f)
.wrapContentHeight(), .wrapContentHeight(),
shape = RoundedCornerShape(24.dp), shape = MaterialTheme.shapes.extraLarge,
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface containerColor = MaterialTheme.colorScheme.surface
), ),
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp) elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -123,30 +126,56 @@ fun LoginScreen(
ErrorCard(message = errorMessage) ErrorCard(message = errorMessage)
// Gradient button
Box(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.clip(MaterialTheme.shapes.medium)
.then(
if (username.isNotEmpty() && password.isNotEmpty() && !isLoading) {
Modifier.background(
Brush.linearGradient(
listOf(
Color(0xFF2563EB),
Color(0xFF8B5CF6)
)
)
)
} else {
Modifier.background(MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.3f))
}
),
contentAlignment = Alignment.Center
) {
Button( Button(
onClick = { onClick = {
viewModel.login(username, password) viewModel.login(username, password)
}, },
modifier = Modifier modifier = Modifier.fillMaxSize(),
.fillMaxWidth()
.height(56.dp),
enabled = username.isNotEmpty() && password.isNotEmpty(), enabled = username.isNotEmpty() && password.isNotEmpty(),
shape = RoundedCornerShape(12.dp) shape = MaterialTheme.shapes.medium,
colors = ButtonDefaults.buttonColors(
containerColor = Color.Transparent,
disabledContainerColor = Color.Transparent
)
) { ) {
if (isLoading) { if (isLoading) {
CircularProgressIndicator( CircularProgressIndicator(
modifier = Modifier.size(24.dp), modifier = Modifier.size(24.dp),
color = MaterialTheme.colorScheme.onPrimary, color = Color.White,
strokeWidth = 2.dp strokeWidth = 2.dp
) )
} else { } else {
Text( Text(
"Sign In", "Sign In",
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold fontWeight = FontWeight.SemiBold,
color = Color.White
) )
} }
} }
}
TextButton( TextButton(
onClick = onNavigateToForgotPassword, onClick = onNavigateToForgotPassword,

View File

@@ -1,9 +1,11 @@
package com.mycrib.android.ui.screens package com.mycrib.android.ui.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
@@ -11,6 +13,9 @@ import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@@ -315,8 +320,11 @@ fun ResidencesScreen(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable { onResidenceClick(residence.id) }, .clickable { onResidenceClick(residence.id) },
shape = RoundedCornerShape(16.dp), shape = MaterialTheme.shapes.large,
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp) elevation = CardDefaults.cardElevation(defaultElevation = 0.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
)
) { ) {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -325,32 +333,67 @@ fun ResidencesScreen(
) { ) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
// Gradient circular house icon
Box(
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
.background(
Brush.linearGradient(
listOf(
Color(0xFF2563EB),
Color(0xFF8B5CF6)
)
)
),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Default.Home,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(28.dp)
)
}
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( Text(
text = residence.name, text = residence.name,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold color = MaterialTheme.colorScheme.onSurface
) )
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
Text( Row(
text = "${residence.streetAddress}", verticalAlignment = Alignment.CenterVertically,
style = MaterialTheme.typography.bodyMedium, horizontalArrangement = Arrangement.spacedBy(4.dp)
color = MaterialTheme.colorScheme.onSurfaceVariant ) {
Icon(
Icons.Default.LocationOn,
contentDescription = null,
modifier = Modifier.size(14.dp),
tint = MaterialTheme.colorScheme.onSurfaceVariant
) )
Text( Text(
text = "${residence.city}, ${residence.stateProvince}", text = "${residence.city}, ${residence.stateProvince}",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
} }
Icon(
Icons.Default.KeyboardArrowRight,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider(color = MaterialTheme.colorScheme.outlineVariant)
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider()
Spacer(modifier = Modifier.height(12.dp))
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@@ -360,19 +403,19 @@ fun ResidencesScreen(
icon = Icons.Default.Assignment, icon = Icons.Default.Assignment,
value = "${residence.taskSummary.total}", value = "${residence.taskSummary.total}",
label = "Tasks", label = "Tasks",
color = MaterialTheme.colorScheme.primary color = Color(0xFF3B82F6)
) )
TaskStatChip( TaskStatChip(
icon = Icons.Default.CheckCircle, icon = Icons.Default.CheckCircle,
value = "${residence.taskSummary.completed}", value = "${residence.taskSummary.completed}",
label = "Done", label = "Done",
color = MaterialTheme.colorScheme.tertiary color = Color(0xFF10B981)
) )
TaskStatChip( TaskStatChip(
icon = Icons.Default.Schedule, icon = Icons.Default.Schedule,
value = "${residence.taskSummary.pending}", value = "${residence.taskSummary.pending}",
label = "Pending", label = "Pending",
color = MaterialTheme.colorScheme.error color = Color(0xFFF59E0B)
) )
} }
} }

View File

@@ -0,0 +1,28 @@
package com.mycrib.android.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
// Modern Shape System - Matching iOS Design System
object AppRadius {
val xs = 4.dp
val sm = 8.dp
val md = 12.dp
val lg = 16.dp
val xl = 20.dp
val xxl = 24.dp
}
val AppShapes = Shapes(
// Small components (buttons, chips, badges)
extraSmall = RoundedCornerShape(AppRadius.xs),
small = RoundedCornerShape(AppRadius.sm),
// Medium components (cards, inputs)
medium = RoundedCornerShape(AppRadius.md),
// Large components (dialogs, sheets)
large = RoundedCornerShape(AppRadius.lg),
extraLarge = RoundedCornerShape(AppRadius.xl)
)

View File

@@ -5,51 +5,73 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
// Bright color palette // Modern Color Palette - Matching iOS Design System
private val BrightBlue = Color(0xFF007AFF) // Primary Colors - Modern blue gradient
private val BrightGreen = Color(0xFF34C759) private val Primary = Color(0xFF2563EB)
private val BrightOrange = Color(0xFFFF9500) private val PrimaryLight = Color(0xFF3B82F6)
private val BrightRed = Color(0xFFFF3B30) private val PrimaryDark = Color(0xFF1E40AF)
private val BrightPurple = Color(0xFFAF52DE)
private val BrightTeal = Color(0xFF5AC8FA)
// Light variations for containers // Accent Colors
private val LightBlue = Color(0xFFE3F2FD) private val Accent = Color(0xFF8B5CF6)
private val LightGreen = Color(0xFFE8F5E9) private val AccentLight = Color(0xFFA78BFA)
private val LightOrange = Color(0xFFFFF3E0)
// Dark variations // Semantic Colors
private val DarkBlue = Color(0xFF0A84FF) private val Success = Color(0xFF10B981)
private val DarkGreen = Color(0xFF30D158) private val Warning = Color(0xFFF59E0B)
private val DarkOrange = Color(0xFFFF9F0A) private val Error = Color(0xFFEF4444)
private val Info = Color(0xFF3B82F6)
// Neutral Colors - Modern grays
private val Background = Color(0xFFF9FAFB)
private val Surface = Color.White
private val SurfaceSecondary = Color(0xFFF3F4F6)
private val TextPrimary = Color(0xFF111827)
private val TextSecondary = Color(0xFF6B7280)
private val TextTertiary = Color(0xFF9CA3AF)
private val Border = Color(0xFFE5E7EB)
private val BorderLight = Color(0xFFF3F4F6)
// Task Status Colors
private val TaskUpcoming = Color(0xFF3B82F6)
private val TaskInProgress = Color(0xFFF59E0B)
private val TaskCompleted = Color(0xFF10B981)
private val TaskCanceled = Color(0xFF6B7280)
private val TaskArchived = Color(0xFF9CA3AF)
// Dark mode variations
private val DarkBackground = Color(0xFF1C1B1F)
private val DarkSurface = Color(0xFF2B2930)
private val DarkSurfaceSecondary = Color(0xFF3B3842)
private val DarkColorScheme = darkColorScheme( private val DarkColorScheme = darkColorScheme(
primary = DarkBlue, primary = PrimaryLight,
onPrimary = Color.White, onPrimary = Color.White,
primaryContainer = Color(0xFF003D75), primaryContainer = PrimaryDark,
onPrimaryContainer = Color(0xFFD0E4FF), onPrimaryContainer = Color(0xFFD0E4FF),
secondary = DarkGreen, secondary = Success,
onSecondary = Color.White, onSecondary = Color.White,
secondaryContainer = Color(0xFF1B5E20), secondaryContainer = Color(0xFF1B5E20),
onSecondaryContainer = Color(0xFFB9F6CA), onSecondaryContainer = Color(0xFFB9F6CA),
tertiary = DarkOrange, tertiary = Warning,
onTertiary = Color.White, onTertiary = Color.White,
tertiaryContainer = Color(0xFF663C00), tertiaryContainer = Color(0xFF663C00),
onTertiaryContainer = Color(0xFFFFE0B2), onTertiaryContainer = Color(0xFFFFE0B2),
error = BrightRed, error = Error,
onError = Color.White, onError = Color.White,
errorContainer = Color(0xFF93000A), errorContainer = Color(0xFF93000A),
onErrorContainer = Color(0xFFFFDAD6), onErrorContainer = Color(0xFFFFDAD6),
background = Color(0xFF1C1B1F), background = DarkBackground,
onBackground = Color(0xFFE6E1E5), onBackground = Color(0xFFE6E1E5),
surface = Color(0xFF1C1B1F), surface = DarkSurface,
onSurface = Color(0xFFE6E1E5), onSurface = Color(0xFFE6E1E5),
surfaceVariant = Color(0xFF49454F), surfaceVariant = DarkSurfaceSecondary,
onSurfaceVariant = Color(0xFFCAC4D0), onSurfaceVariant = Color(0xFFCAC4D0),
outline = Color(0xFF938F99), outline = Color(0xFF938F99),
@@ -57,36 +79,36 @@ private val DarkColorScheme = darkColorScheme(
) )
private val LightColorScheme = lightColorScheme( private val LightColorScheme = lightColorScheme(
primary = BrightBlue, primary = Primary,
onPrimary = Color.White, onPrimary = Color.White,
primaryContainer = LightBlue, primaryContainer = Color(0xFFEBF2FF),
onPrimaryContainer = Color(0xFF001D35), onPrimaryContainer = PrimaryDark,
secondary = BrightGreen, secondary = Success,
onSecondary = Color.White, onSecondary = Color.White,
secondaryContainer = LightGreen, secondaryContainer = Color(0xFFD1FAE5),
onSecondaryContainer = Color(0xFF002106), onSecondaryContainer = Color(0xFF064E3B),
tertiary = BrightOrange, tertiary = Warning,
onTertiary = Color.White, onTertiary = Color.White,
tertiaryContainer = LightOrange, tertiaryContainer = Color(0xFFFEF3C7),
onTertiaryContainer = Color(0xFF2B1700), onTertiaryContainer = Color(0xFF78350F),
error = BrightRed, error = Error,
onError = Color.White, onError = Color.White,
errorContainer = Color(0xFFFFDAD6), errorContainer = Color(0xFFFEE2E2),
onErrorContainer = Color(0xFF410002), onErrorContainer = Color(0xFF7F1D1D),
background = Color(0xFFFFFBFE), background = Background,
onBackground = Color(0xFF1C1B1F), onBackground = TextPrimary,
surface = Color(0xFFFFFBFE), surface = Surface,
onSurface = Color(0xFF1C1B1F), onSurface = TextPrimary,
surfaceVariant = Color(0xFFE7E0EC), surfaceVariant = SurfaceSecondary,
onSurfaceVariant = Color(0xFF49454F), onSurfaceVariant = TextSecondary,
outline = Color(0xFF79747E), outline = Border,
outlineVariant = Color(0xFFCAC4D0) outlineVariant = BorderLight
) )
@Composable @Composable
@@ -98,6 +120,20 @@ fun MyCribTheme(
MaterialTheme( MaterialTheme(
colorScheme = colorScheme, colorScheme = colorScheme,
typography = AppTypography,
shapes = AppShapes,
content = content content = content
) )
} }
// Extended colors for semantic states (not part of Material3 ColorScheme)
object AppColors {
val accent = Accent
val accentLight = AccentLight
val info = Info
val taskUpcoming = TaskUpcoming
val taskInProgress = TaskInProgress
val taskCompleted = TaskCompleted
val taskCanceled = TaskCanceled
val taskArchived = TaskArchived
}

View File

@@ -0,0 +1,94 @@
package com.mycrib.android.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Modern Typography Scale - Matching iOS Design System
val AppTypography = Typography(
// Display - For hero sections
displayLarge = TextStyle(
fontSize = 57.sp,
fontWeight = FontWeight.Bold,
lineHeight = 64.sp
),
displayMedium = TextStyle(
fontSize = 45.sp,
fontWeight = FontWeight.Bold,
lineHeight = 52.sp
),
displaySmall = TextStyle(
fontSize = 36.sp,
fontWeight = FontWeight.Bold,
lineHeight = 44.sp
),
// Headline - For section headers
headlineLarge = TextStyle(
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
lineHeight = 40.sp
),
headlineMedium = TextStyle(
fontSize = 28.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 36.sp
),
headlineSmall = TextStyle(
fontSize = 24.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 32.sp
),
// Title - For card titles
titleLarge = TextStyle(
fontSize = 22.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 28.sp
),
titleMedium = TextStyle(
fontSize = 18.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 24.sp
),
titleSmall = TextStyle(
fontSize = 16.sp,
fontWeight = FontWeight.SemiBold,
lineHeight = 20.sp
),
// Body - For main content
bodyLarge = TextStyle(
fontSize = 17.sp,
fontWeight = FontWeight.Normal,
lineHeight = 24.sp
),
bodyMedium = TextStyle(
fontSize = 15.sp,
fontWeight = FontWeight.Normal,
lineHeight = 20.sp
),
bodySmall = TextStyle(
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
lineHeight = 16.sp
),
// Label - For labels and captions
labelLarge = TextStyle(
fontSize = 14.sp,
fontWeight = FontWeight.Medium,
lineHeight = 20.sp
),
labelMedium = TextStyle(
fontSize = 12.sp,
fontWeight = FontWeight.Medium,
lineHeight = 16.sp
),
labelSmall = TextStyle(
fontSize = 11.sp,
fontWeight = FontWeight.Medium,
lineHeight = 16.sp
)
)