From b88d767571aba0147d57ef83d8367b8ff7b4d0d8 Mon Sep 17 00:00:00 2001 From: nuryuda Date: Fri, 7 Nov 2025 17:02:27 +0700 Subject: [PATCH] UTS --- README.md | 55 +- .../java/com/example/tiptime/MainActivity.kt | 788 ++++++++++++++---- 2 files changed, 698 insertions(+), 145 deletions(-) diff --git a/README.md b/README.md index 08d4aa4..a73486b 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,57 @@ Petunjuk lebih detil dapat dibaca di https://docs.google.com/document/d/1iGiC0Bg3Bdcd2Maq45TYkCDUkZ5Ql51E/edit?rtpof=true Starter dimodifikasi dan terinspirasi dari: -https://developer.android.com/codelabs/basic-android-compose-calculate-tip#0 \ No newline at end of file +https://developer.android.com/codelabs/basic-android-compose-calculate-tip#0 +# 🧮 BMI Calculator (Metrik & Imperial) + +Sebuah aplikasi modern dan interaktif untuk menghitung **Body Mass Index (BMI)** menggunakan dua sistem satuan: +🌍 **Metrik (kg, m)** dan 🇺🇸 **Imperial (lbs, inch)**. + +Aplikasi ini membantu kamu mengetahui apakah berat badanmu tergolong **kurus**, **ideal**, **berlebih**, atau **obesitas** berdasarkan standar kesehatan internasional (WHO). + +--- + +## ✨ Fitur Utama + +✅ Hitung BMI dengan **dua pilihan sistem satuan**: +- **Metrik** → kilogram (kg) & meter (m) +- **Imperial** → pound (lbs) & inch (in) + +✅ Menampilkan hasil **BMI secara otomatis** +✅ Menentukan **kategori berat badan** sesuai standar WHO +✅ Antarmuka **sederhana, responsif, dan mudah digunakan** +✅ Dapat dijalankan di **desktop maupun mobile** +✅ Dikembangkan dengan bantuan **AI cerdas (ChatGPT & Claude)** + +--- + + + +## 🧠 Kategori BMI + +| Kategori | Rentang Nilai BMI | +|------------------------------|------------------| +| Kurang / Underweight | < 18.5 | +| Normal / Ideal | 18.5 – 24.9 | +| Kelebihan berat badan / Overweight | 25.0 – 29.9 | +| Obesitas / Obese | ≥ 30.0 | + +--- + +## 🚀 Cara Menggunakan + +1. Pilih sistem satuan: **Metrik** atau **Imperial** +2. Masukkan berat badan dan tinggi badan sesuai sistem satuan +3. Klik tombol **Hitung / Calculate** +4. Lihat hasil **BMI** dan kategori kesehatannya + +--- + +## 💻 Contoh Penggunaan + +### 🌍 **Metrik** + +Aplikasi Ini Di Bantu Oleh +-ChatGpt +-Claude Ai +Satrio Putra Wardani 202310715307 diff --git a/app/src/main/java/com/example/tiptime/MainActivity.kt b/app/src/main/java/com/example/tiptime/MainActivity.kt index d0fdd80..ddee870 100644 --- a/app/src/main/java/com/example/tiptime/MainActivity.kt +++ b/app/src/main/java/com/example/tiptime/MainActivity.kt @@ -1,18 +1,3 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.example.tiptime import android.os.Bundle @@ -20,42 +5,42 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawingPadding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.animation.* +import androidx.compose.animation.core.* +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Switch -import androidx.compose.material3.Text -import androidx.compose.material3.TextField -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.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import com.example.tiptime.ui.theme.TipTimeTheme -import java.text.NumberFormat +import kotlin.math.pow +import java.text.DecimalFormat + +// Warna tema klasik +private val ClassicGold = Color(0xFFD4AF37) +private val ClassicCream = Color(0xFFFFF8DC) +private val ClassicBrown = Color(0xFF8B4513) +private val ClassicDarkBrown = Color(0xFF654321) +private val ClassicBeige = Color(0xFFF5F5DC) class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { @@ -63,10 +48,14 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { TipTimeTheme { - Surface( - modifier = Modifier.fillMaxSize(), - ) { - TipTimeLayout() + Surface(modifier = Modifier.fillMaxSize()) { + var showWelcome by remember { mutableStateOf(true) } + + if (showWelcome) { + WelcomeScreen(onContinue = { showWelcome = false }) + } else { + BmiCalculatorLayout(onBackPressed = { showWelcome = true }) + } } } } @@ -74,141 +63,652 @@ class MainActivity : ComponentActivity() { } @Composable -fun TipTimeLayout() { - var amountInput by remember { mutableStateOf("") } - var tipInput by remember { mutableStateOf("") } - var roundUp by remember { mutableStateOf(false) } +fun WelcomeScreen(onContinue: () -> Unit) { + var visible by remember { mutableStateOf(false) } - val BmiHeight = amountInput.toDoubleOrNull() ?: 0.0 - val BmiWeight = tipInput.toDoubleOrNull() ?: 0.0 - val bmi = calculateBMI(BmiHeight, BmiWeight, roundUp) - val category = calculateBMICategory(BmiHeight, BmiWeight, roundUp) + LaunchedEffect(Unit) { + visible = true + } - Column( + Box( modifier = Modifier - .statusBarsPadding() - .padding(horizontal = 40.dp) - .verticalScroll(rememberScrollState()) - .safeDrawingPadding(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center + .fillMaxSize() + .background( + Brush.verticalGradient( + colors = listOf( + ClassicDarkBrown, + ClassicBrown, + ClassicBeige + ) + ) + ) ) { - Text( - text = stringResource(R.string.calculate_tip), + Column( modifier = Modifier - .padding(bottom = 16.dp, top = 40.dp) - .align(alignment = Alignment.Start) - ) - EditNumberField( - label = R.string.height, - leadingIcon = R.drawable.number, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next - ), - value = amountInput, - onValueChanged = { amountInput = it }, - modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), - ) - EditNumberField( - label = R.string.weight, - leadingIcon = R.drawable.number, - keyboardOptions = KeyboardOptions.Default.copy( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Done - ), - value = tipInput, - onValueChanged = { tipInput = it }, - modifier = Modifier.padding(bottom = 32.dp).fillMaxWidth(), - ) - RoundTheTipRow( - roundUp = roundUp, - onRoundUpChanged = { roundUp = it }, - modifier = Modifier.padding(bottom = 32.dp) - ) - Text( - text = stringResource(R.string.bmi_calculation, bmi), - style = MaterialTheme.typography.displaySmall - ) - Text( - text = stringResource(R.string.bmi_category, category), - style = MaterialTheme.typography.displaySmall - ) + .fillMaxSize() + .statusBarsPadding() + .padding(32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween + ) { + // Spacer untuk centering + Spacer(modifier = Modifier.height(60.dp)) - Spacer(modifier = Modifier.height(150.dp)) + // Content utama + AnimatedVisibility( + visible = visible, + enter = fadeIn(animationSpec = tween(1000)) + + scaleIn(initialScale = 0.8f, animationSpec = tween(1000)) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + // Icon/Logo + Card( + modifier = Modifier + .size(140.dp) + .shadow(16.dp, RoundedCornerShape(70.dp)), + shape = RoundedCornerShape(70.dp), + colors = CardDefaults.cardColors( + containerColor = ClassicGold + ), + border = BorderStroke(4.dp, ClassicCream) + ) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier.fillMaxSize() + ) { + Text( + text = "💪", + fontSize = 72.sp + ) + } + } + + Spacer(modifier = Modifier.height(40.dp)) + + // Ornamen atas + Text( + text = "✦ ═══════════════ ✦", + color = ClassicGold, + fontSize = 18.sp, + modifier = Modifier.padding(bottom = 20.dp) + ) + + // Judul utama + Text( + text = "KALKULATOR", + fontSize = 32.sp, + fontWeight = FontWeight.Bold, + color = ClassicGold, + letterSpacing = 5.sp + ) + + Text( + text = "BMI Satrio Putra Wardani", + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + color = ClassicCream, + letterSpacing = 3.sp, + modifier = Modifier.padding(vertical = 12.dp) + ) + + // Ornamen bawah + Text( + text = "✦ ═══════════════ ✦", + color = ClassicGold, + fontSize = 18.sp, + modifier = Modifier.padding(bottom = 32.dp) + ) + + // Deskripsi + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors( + containerColor = ClassicCream.copy(alpha = 0.15f) + ), + border = BorderStroke(1.5.dp, ClassicGold.copy(alpha = 0.4f)) + ) { + Column( + modifier = Modifier.padding(28.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Hitung Indeks Massa Tubuh Anda", + fontSize = 19.sp, + fontWeight = FontWeight.SemiBold, + color = ClassicCream, + textAlign = TextAlign.Center, + modifier = Modifier.padding(bottom = 16.dp) + ) + + Text( + text = "Ketahui status kesehatan Anda dengan perhitungan BMI yang akurat dan mudah", + fontSize = 15.sp, + color = ClassicCream.copy(alpha = 0.85f), + textAlign = TextAlign.Center, + lineHeight = 22.sp + ) + } + } + } + } + + // Tombol dan footer + AnimatedVisibility( + visible = visible, + enter = fadeIn(animationSpec = tween(1000, delayMillis = 500)) + + slideInVertically( + initialOffsetY = { 50 }, + animationSpec = tween(1000, delayMillis = 500) + ) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(bottom = 8.dp) + ) { + Button( + onClick = onContinue, + modifier = Modifier + .fillMaxWidth() + .height(60.dp) + .shadow(12.dp, RoundedCornerShape(30.dp)), + shape = RoundedCornerShape(30.dp), + colors = ButtonDefaults.buttonColors( + containerColor = ClassicGold, + contentColor = ClassicDarkBrown + ), + border = BorderStroke(2.5.dp, ClassicCream) + ) { + Text( + text = "MULAI PERHITUNGAN", + fontSize = 19.sp, + fontWeight = FontWeight.Bold, + letterSpacing = 1.5.sp + ) + } + + Spacer(modifier = Modifier.height(28.dp)) + + // Divider dekoratif + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(horizontal = 32.dp) + ) { + Box( + modifier = Modifier + .weight(1f) + .height(1.dp) + .background(ClassicCream.copy(alpha = 0.3f)) + ) + Text( + text = " ✦ ", + color = ClassicGold.copy(alpha = 0.6f), + fontSize = 16.sp + ) + Box( + modifier = Modifier + .weight(1f) + .height(1.dp) + .background(ClassicCream.copy(alpha = 0.3f)) + ) + } + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = "Universitas Bhayangkara Jakarta Raya", + fontSize = 13.sp, + color = ClassicDarkBrown.copy(alpha = 0.5f), + letterSpacing = 1.sp + ) + + Text( + text = "SATRIO PUTRA WARDANI", + fontSize = 15.sp, + fontWeight = FontWeight.Bold, + color = ClassicGold.copy(alpha = 0.8f), + letterSpacing = 2.sp, + modifier = Modifier.padding(top = 4.dp) + ) + } + } + } } } @Composable -fun EditNumberField( - @StringRes label: Int, +fun BmiCalculatorLayout(onBackPressed: () -> Unit) { + var heightInput by remember { mutableStateOf("") } + var weightInput by remember { mutableStateOf("") } + var useImperial by remember { mutableStateOf(false) } + + val height = heightInput.toDoubleOrNull() ?: 0.0 + val weight = weightInput.toDoubleOrNull() ?: 0.0 + + val bmi = calculateBMI(height, weight, useImperial) + val category = getBMICategory(bmi) + + Box( + modifier = Modifier + .fillMaxSize() + .background( + Brush.verticalGradient( + colors = listOf( + ClassicCream, + ClassicBeige + ) + ) + ) + ) { + Column( + modifier = Modifier + .statusBarsPadding() + .padding(20.dp) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // Tombol Back + Row( + modifier = Modifier + .fillMaxWidth() + .padding(bottom = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + IconButton( + onClick = onBackPressed, + modifier = Modifier + .shadow(4.dp, RoundedCornerShape(12.dp)) + .background(ClassicGold, RoundedCornerShape(12.dp)) + .size(48.dp) + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Kembali", + tint = ClassicDarkBrown + ) + } + + Spacer(modifier = Modifier.width(12.dp)) + + Text( + text = "Kembali ke Beranda", + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = ClassicDarkBrown + ) + } + + // Header dengan ornamen klasik + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp) + .shadow(8.dp, RoundedCornerShape(16.dp)), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors( + containerColor = ClassicDarkBrown + ), + border = BorderStroke(2.dp, ClassicGold) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(24.dp) + ) { + Text( + text = "═══════════════", + color = ClassicGold, + fontSize = 20.sp + ) + Text( + text = "KALKULATOR BMI", + fontSize = 32.sp, + fontWeight = FontWeight.Bold, + color = ClassicGold, + letterSpacing = 2.sp, + modifier = Modifier.padding(vertical = 8.dp) + ) + Text( + text = "═══════════════", + color = ClassicGold, + fontSize = 20.sp + ) + Text( + text = "Body Mass Index Calculator", + fontSize = 14.sp, + color = ClassicCream, + fontWeight = FontWeight.Light, + modifier = Modifier.padding(top = 8.dp) + ) + } + } + + // Form Input Card + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + .shadow(6.dp, RoundedCornerShape(12.dp)), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = Color.White + ), + border = BorderStroke(1.dp, ClassicGold) + ) { + Column( + modifier = Modifier.padding(20.dp) + ) { + Text( + text = "Data Pengukuran", + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + color = ClassicDarkBrown, + modifier = Modifier.padding(bottom = 16.dp) + ) + + ClassicTextField( + label = if (useImperial) "Tinggi (inch)" else "Tinggi (cm)", + leadingIcon = R.drawable.number, + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + value = heightInput, + onValueChanged = { heightInput = it }, + modifier = Modifier + .padding(bottom = 16.dp) + .fillMaxWidth() + ) + + ClassicTextField( + label = if (useImperial) "Berat (lbs)" else "Berat (kg)", + leadingIcon = R.drawable.number, + keyboardOptions = KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Done + ), + value = weightInput, + onValueChanged = { weightInput = it }, + modifier = Modifier + .padding(bottom = 16.dp) + .fillMaxWidth() + ) + + HorizontalDivider( + color = ClassicGold, + thickness = 1.dp, + modifier = Modifier.padding(vertical = 8.dp) + ) + + ClassicUnitSwitch( + useImperial = useImperial, + onUnitChange = { useImperial = it } + ) + } + } + + // Result Card + if (height > 0 && weight > 0) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + .shadow(8.dp, RoundedCornerShape(12.dp)), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = ClassicDarkBrown + ), + border = BorderStroke(2.dp, ClassicGold) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxWidth() + .padding(28.dp) + ) { + Text( + text = "— Hasil Perhitungan —", + fontSize = 16.sp, + color = ClassicGold, + fontWeight = FontWeight.Medium, + modifier = Modifier.padding(bottom = 16.dp) + ) + + Text( + text = "INDEKS MASSA TUBUH", + fontSize = 14.sp, + color = ClassicCream, + letterSpacing = 1.sp, + modifier = Modifier.padding(bottom = 8.dp) + ) + + Text( + text = DecimalFormat("#.##").format(bmi), + fontSize = 56.sp, + fontWeight = FontWeight.Bold, + color = ClassicGold, + modifier = Modifier.padding(vertical = 8.dp) + ) + + Box( + modifier = Modifier + .background( + color = getCategoryColor(category), + shape = RoundedCornerShape(24.dp) + ) + .padding(horizontal = 24.dp, vertical = 12.dp) + ) { + Text( + text = category.uppercase(), + fontSize = 20.sp, + color = Color.White, + fontWeight = FontWeight.Bold, + letterSpacing = 1.sp + ) + } + + Text( + text = getCategoryDescription(category), + fontSize = 13.sp, + color = ClassicCream, + textAlign = TextAlign.Center, + modifier = Modifier.padding(top = 16.dp) + ) + } + } + + // Info Card + BMICategoryInfo() + } + + Spacer(modifier = Modifier.height(40.dp)) + } + } +} + +@Composable +fun ClassicTextField( + label: String, @DrawableRes leadingIcon: Int, keyboardOptions: KeyboardOptions, value: String, onValueChanged: (String) -> Unit, modifier: Modifier = Modifier ) { - TextField( + OutlinedTextField( value = value, - singleLine = true, - leadingIcon = { Icon(painter = painterResource(id = leadingIcon), null) }, - modifier = modifier, onValueChange = onValueChanged, - label = { Text(stringResource(label)) }, - keyboardOptions = keyboardOptions + label = { + Text( + label, + color = ClassicBrown, + fontWeight = FontWeight.Medium + ) + }, + singleLine = true, + leadingIcon = { + Icon( + painter = painterResource(id = leadingIcon), + contentDescription = null, + tint = ClassicGold + ) + }, + keyboardOptions = keyboardOptions, + modifier = modifier, + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = ClassicGold, + unfocusedBorderColor = ClassicBrown.copy(alpha = 0.5f), + focusedTextColor = ClassicDarkBrown, + unfocusedTextColor = ClassicDarkBrown + ), + shape = RoundedCornerShape(8.dp) ) } @Composable -fun RoundTheTipRow( - roundUp: Boolean, - onRoundUpChanged: (Boolean) -> Unit, - modifier: Modifier = Modifier +fun ClassicUnitSwitch( + useImperial: Boolean, + onUnitChange: (Boolean) -> Unit ) { Row( - modifier = modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween ) { - Text(text = stringResource(R.string.use_usc)) + Text( + "Satuan Imperial (US)", + color = ClassicDarkBrown, + fontWeight = FontWeight.Medium + ) Switch( - modifier = Modifier - .fillMaxWidth() - .wrapContentWidth(Alignment.End), - checked = roundUp, - onCheckedChange = onRoundUpChanged + checked = useImperial, + onCheckedChange = onUnitChange, + colors = SwitchDefaults.colors( + checkedThumbColor = ClassicGold, + checkedTrackColor = ClassicGold.copy(alpha = 0.5f), + uncheckedThumbColor = ClassicBrown, + uncheckedTrackColor = ClassicBrown.copy(alpha = 0.3f) + ) ) } } -/** - * Calculates the BMI - * - * Catatan: tambahkan unit test untuk kalkulasi BMI ini - */ -private fun calculateBMI(BmiHeight: Double, BmiWeight: Double = 15.0, roundUp: Boolean): String { - var bmi = BmiWeight / 100 * BmiHeight - if (roundUp) { - bmi = kotlin.math.ceil(bmi) - } - return NumberFormat.getNumberInstance().format(bmi) -} -/** - * Calculates the BMI Category - * - * Catatan: tambahkan unit test untuk kalkulasi BMI ini - */ +@Composable +fun BMICategoryInfo() { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + .shadow(4.dp, RoundedCornerShape(12.dp)), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = Color.White + ), + border = BorderStroke(1.dp, ClassicGold.copy(alpha = 0.3f)) + ) { + Column( + modifier = Modifier.padding(20.dp) + ) { + Text( + text = "Kategori BMI", + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = ClassicDarkBrown, + modifier = Modifier.padding(bottom = 12.dp) + ) -private fun calculateBMICategory(BmiHeight: Double, BmiWeight: Double = 15.0, roundUp: Boolean): String { - var bmi = BmiWeight / 100 * BmiHeight - if (roundUp) { - bmi = kotlin.math.ceil(bmi) + CategoryRow("Kurus", "< 18.5", Color(0xFF4A90E2)) + CategoryRow("Normal", "18.5 - 24.9", Color(0xFF50C878)) + CategoryRow("Gemuk", "25.0 - 29.9", Color(0xFFFF9500)) + CategoryRow("Obesitas", "≥ 30.0", Color(0xFFE74C3C)) + } } - return NumberFormat.getNumberInstance().format(bmi) } + +@Composable +fun CategoryRow(category: String, range: String, color: Color) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 6.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(12.dp) + .background(color, RoundedCornerShape(2.dp)) + ) + Spacer(modifier = Modifier.width(12.dp)) + Text( + text = category, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = ClassicDarkBrown, + modifier = Modifier.weight(1f) + ) + Text( + text = range, + fontSize = 14.sp, + color = ClassicBrown, + fontWeight = FontWeight.Normal + ) + } +} + +fun getCategoryColor(category: String): Color { + return when (category) { + "Kurus" -> Color(0xFF4A90E2) + "Normal" -> Color(0xFF50C878) + "Gemuk" -> Color(0xFFFF9500) + "Obesitas" -> Color(0xFFE74C3C) + else -> ClassicBrown + } +} + +fun getCategoryDescription(category: String): String { + return when (category) { + "Kurus" -> "Berat badan Anda kurang dari ideal" + "Normal" -> "Berat badan Anda ideal dan sehat" + "Gemuk" -> "Berat badan Anda melebihi ideal" + "Obesitas" -> "Konsultasikan dengan dokter Anda" + else -> "" + } +} + +fun calculateBMI(height: Double, weight: Double, useImperial: Boolean): Double { + if (height <= 0 || weight <= 0) return 0.0 + return if (useImperial) { + 703 * weight / height.pow(2) + } else { + weight / (height / 100).pow(2) + } +} + +fun getBMICategory(bmi: Double): String { + return when { + bmi == 0.0 -> "-" + bmi < 18.5 -> "Kurus" + bmi < 25 -> "Normal" + bmi < 30 -> "Gemuk" + else -> "Obesitas" + } +} + @Preview(showBackground = true) @Composable -fun TipTimeLayoutPreview() { +fun WelcomeScreenPreview() { TipTimeTheme { - TipTimeLayout() + WelcomeScreen(onContinue = {}) + } +} + +@Preview(showBackground = true) +@Composable +fun BmiCalculatorPreview() { + TipTimeTheme { + BmiCalculatorLayout(onBackPressed = {}) } } \ No newline at end of file