Merge pull request 'Android redesign: v1.4.0' (#28) from feat/android-app-redesign into master
Reviewed-on: #28 Reviewed-by: rafaga21 <rafaelminaya20@hotmail.com>
This commit was merged in pull request #28.
This commit is contained in:
@@ -12,8 +12,8 @@ android {
|
||||
applicationId = "com.devlab.app"
|
||||
minSdk = 26
|
||||
targetSdk = 37
|
||||
versionCode = 4
|
||||
versionName = "1.3.0"
|
||||
versionCode = 5
|
||||
versionName = "1.4.0"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
||||
@@ -299,7 +299,14 @@ fun AppRoot() {
|
||||
|
||||
composable(Screen.Profile.route) {
|
||||
ProfileScreen(
|
||||
onBack = { navController.popBackStack() }
|
||||
onBack = { navController.popBackStack() },
|
||||
onLogout = {
|
||||
loginViewModel.logout()
|
||||
isLoggedIn = false
|
||||
navController.navigate(Screen.Login.route) {
|
||||
popUpTo(0) { inclusive = true }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package com.devlab.app.ui.dashboard
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
@@ -9,7 +11,8 @@ import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -28,33 +31,63 @@ fun DashboardScreen(
|
||||
viewModel: DashboardViewModel = viewModel()
|
||||
) {
|
||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val context = LocalContext.current
|
||||
val context = androidx.compose.ui.platform.LocalContext.current
|
||||
val appVersion = remember {
|
||||
try {
|
||||
context.packageManager.getPackageInfo(context.packageName, 0).versionName ?: "1.2.0"
|
||||
} catch (_: Exception) { "1.2.0" }
|
||||
context.packageManager.getPackageInfo(context.packageName, 0).versionName ?: "1.3.0"
|
||||
} catch (_: Exception) { "1.3.0" }
|
||||
}
|
||||
val initial = state.username.firstOrNull()?.uppercase() ?: "?"
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Column {
|
||||
Text("Dashboard")
|
||||
if (state.username.isNotBlank()) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.primary,
|
||||
MaterialTheme.colorScheme.primary.copy(alpha = 0.85f)
|
||||
)
|
||||
)
|
||||
)
|
||||
.padding(top = 48.dp, start = 16.dp, end = 16.dp, bottom = 24.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.size(44.dp),
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.2f)
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Text(
|
||||
text = "Welcome, ${state.username}",
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.8f)
|
||||
text = initial,
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = "Dashboard",
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
if (state.username.isNotBlank()) {
|
||||
Text(
|
||||
text = "Welcome, ${state.username}",
|
||||
color = MaterialTheme.colorScheme.onPrimary.copy(alpha = 0.8f),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
titleContentColor = MaterialTheme.colorScheme.onPrimary
|
||||
),
|
||||
actions = {
|
||||
IconButton(onClick = { onProfileClick() }) {
|
||||
Icon(
|
||||
Icons.Default.AccountCircle,
|
||||
@@ -70,47 +103,26 @@ fun DashboardScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
|
||||
when {
|
||||
state.isLoading -> {
|
||||
LoadingIndicator(modifier = Modifier.padding(padding))
|
||||
}
|
||||
state.isLoading -> LoadingIndicator()
|
||||
state.error != null -> {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Icon(
|
||||
Icons.Default.ErrorOutline,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(48.dp),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
Icon(Icons.Default.ErrorOutline, contentDescription = null, modifier = Modifier.size(48.dp), tint = MaterialTheme.colorScheme.error)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = state.error ?: "",
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
)
|
||||
Text(text = state.error ?: "", color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodyLarge)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
OutlinedButton(onClick = { viewModel.loadStats() }) {
|
||||
Text("Retry")
|
||||
}
|
||||
OutlinedButton(onClick = { viewModel.loadStats() }) { Text("Retry") }
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val dashboardStats = state.stats
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
) {
|
||||
@@ -120,75 +132,21 @@ fun DashboardScreen(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Text(
|
||||
text = "Servers",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
Text(text = "Servers", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.SemiBold)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
StatCard(
|
||||
title = "Total",
|
||||
value = "${dashboardStats?.stats?.total_servers ?: 0}",
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
icon = Icons.Default.Dns,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
StatCard(
|
||||
title = "Online",
|
||||
value = "${dashboardStats?.stats?.online_servers ?: 0}",
|
||||
color = Green500,
|
||||
icon = Icons.Default.CheckCircle,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
StatCard(
|
||||
title = "Offline",
|
||||
value = "${dashboardStats?.stats?.offline_servers ?: 0}",
|
||||
color = Red500,
|
||||
icon = Icons.Default.Cancel,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
StatCard(title = "Total", value = "${dashboardStats?.stats?.total_servers ?: 0}", color = MaterialTheme.colorScheme.primary, icon = Icons.Default.Dns, modifier = Modifier.weight(1f))
|
||||
StatCard(title = "Online", value = "${dashboardStats?.stats?.online_servers ?: 0}", color = Green500, icon = Icons.Default.CheckCircle, modifier = Modifier.weight(1f))
|
||||
StatCard(title = "Offline", value = "${dashboardStats?.stats?.offline_servers ?: 0}", color = Red500, icon = Icons.Default.Cancel, modifier = Modifier.weight(1f))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
Text(
|
||||
text = "Average Usage",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
)
|
||||
Text(text = "Average Usage", style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.SemiBold)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
StatCard(
|
||||
title = "CPU",
|
||||
value = "${dashboardStats?.stats?.avg_cpu?.toInt() ?: 0}%",
|
||||
color = Orange500,
|
||||
icon = Icons.Default.Memory,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
StatCard(
|
||||
title = "RAM",
|
||||
value = "${dashboardStats?.stats?.avg_ram?.toInt() ?: 0}%",
|
||||
color = Blue700,
|
||||
icon = Icons.Default.Storage,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
StatCard(
|
||||
title = "Disk",
|
||||
value = "${dashboardStats?.stats?.avg_disk?.toInt() ?: 0}%",
|
||||
color = Green500,
|
||||
icon = Icons.Default.SdStorage,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
StatCard(title = "CPU", value = "${dashboardStats?.stats?.avg_cpu?.toInt() ?: 0}%", color = Orange500, icon = Icons.Default.Memory, modifier = Modifier.weight(1f))
|
||||
StatCard(title = "RAM", value = "${dashboardStats?.stats?.avg_ram?.toInt() ?: 0}%", color = Blue700, icon = Icons.Default.Storage, modifier = Modifier.weight(1f))
|
||||
StatCard(title = "Disk", value = "${dashboardStats?.stats?.avg_disk?.toInt() ?: 0}%", color = Green500, icon = Icons.Default.SdStorage, modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
@@ -69,8 +70,16 @@ fun NotificationsScreen(
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = Modifier.background(
|
||||
androidx.compose.ui.graphics.Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.primary,
|
||||
MaterialTheme.colorScheme.primary.copy(alpha = 0.85f)
|
||||
)
|
||||
)
|
||||
),
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
containerColor = androidx.compose.ui.graphics.Color.Transparent,
|
||||
titleContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
@Composable
|
||||
fun ProfileScreen(
|
||||
onBack: () -> Unit,
|
||||
onLogout: () -> Unit = {},
|
||||
viewModel: ProfileViewModel = viewModel()
|
||||
) {
|
||||
val state by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
@@ -299,6 +300,26 @@ fun ProfileScreen(
|
||||
Text("Save Changes", fontWeight = FontWeight.SemiBold)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
|
||||
OutlinedButton(
|
||||
onClick = onLogout,
|
||||
modifier = Modifier.fillMaxWidth().height(50.dp),
|
||||
colors = ButtonDefaults.outlinedButtonColors(
|
||||
contentColor = MaterialTheme.colorScheme.error
|
||||
)
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.Logout,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text("Sign Out", fontWeight = FontWeight.SemiBold)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.devlab.app.ui.servers
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
@@ -48,6 +49,14 @@ fun ServerDetailScreen(
|
||||
containerColor = MaterialTheme.colorScheme.primary,
|
||||
titleContentColor = MaterialTheme.colorScheme.onPrimary,
|
||||
navigationIconContentColor = MaterialTheme.colorScheme.onPrimary
|
||||
),
|
||||
modifier = Modifier.background(
|
||||
Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.primary,
|
||||
MaterialTheme.colorScheme.primary.copy(alpha = 0.85f)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -72,11 +81,12 @@ fun ServerDetailScreen(
|
||||
}
|
||||
else -> {
|
||||
Column(modifier = Modifier.fillMaxSize().padding(padding)) {
|
||||
TabRow(
|
||||
ScrollableTabRow(
|
||||
selectedTabIndex = state.selectedTab,
|
||||
containerColor = MaterialTheme.colorScheme.surface,
|
||||
contentColor = MaterialTheme.colorScheme.primary,
|
||||
divider = {}
|
||||
divider = {},
|
||||
edgePadding = 8.dp
|
||||
) {
|
||||
Tab(
|
||||
selected = state.selectedTab == 0,
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user