diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index af7c6d3..902bab3 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -18,10 +18,42 @@ kotlin { } jvm("desktop") - + sourceSets { - val desktopMain by getting - + val exposedVersion = "0.61.0" + + val desktopMain by getting { + dependencies { + implementation(compose.desktop.currentOs) + implementation(libs.kotlinx.coroutines.swing) + implementation("io.ktor:ktor-client-okhttp:2.3.2") + + implementation("org.postgresql:postgresql:42.7.1") + + implementation("org.jetbrains.exposed:exposed-core:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-crypt:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-dao:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-jdbc:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-jodatime:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-java-time:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-kotlin-datetime:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-json:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-money:$exposedVersion") + implementation("org.jetbrains.exposed:exposed-spring-boot-starter:$exposedVersion") + + implementation("io.ktor:ktor-server-core:2.3.4") + implementation("io.ktor:ktor-server-netty:2.3.4") + implementation("io.ktor:ktor-server-content-negotiation:2.3.4") + implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.4") + implementation("io.ktor:ktor-server-status-pages:2.3.4") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") + } + } + + val commonMain by getting { + resources.srcDir("src/commonMain/composeResources") + } + androidMain.dependencies { implementation(compose.preview) implementation(libs.androidx.activity.compose) @@ -40,10 +72,16 @@ kotlin { implementation("cafe.adriel.voyager:voyager-screenmodel:$voyagerVersion") implementation(compose.preview) implementation(libs.androidx.lifecycle.viewmodel) - } - desktopMain.dependencies { - implementation(compose.desktop.currentOs) - implementation(libs.kotlinx.coroutines.swing) + implementation("media.kamel:kamel-image:0.7.0") + implementation("io.ktor:ktor-client-core:2.3.2") + implementation("io.ktor:ktor-client-cio:2.3.2") + implementation("io.ktor:ktor-client-okhttp:2.3.2") + implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2") + + repositories { + google() + mavenCentral() + } } } } @@ -72,8 +110,12 @@ android { compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 + isCoreLibraryDesugaringEnabled = true } } +dependencies { + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") +} compose.desktop { application { diff --git a/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/DatePickerField.kt b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/DatePickerField.kt new file mode 100644 index 0000000..8515410 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/DatePickerField.kt @@ -0,0 +1,33 @@ +package org.example.project.ViewModel + +import android.app.DatePickerDialog +import android.widget.DatePicker +import androidx.compose.runtime.* +import androidx.compose.ui.platform.LocalContext +import java.util.* + +@Composable +actual fun DatePickerField( + onDateSelected: (String) -> Unit, + trigger: Boolean, + onDismissRequest: () -> Unit +) { + val context = LocalContext.current + + if (trigger) { + LaunchedEffect(Unit) { + val calendar = Calendar.getInstance() + DatePickerDialog( + context, + { _: DatePicker, year: Int, month: Int, dayOfMonth: Int -> + val formatted = "%02d.%02d.%04d".format(dayOfMonth, month + 1, year) + onDateSelected(formatted) + onDismissRequest() + }, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH) + ).show() + } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/GetPath.kt b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/GetPath.kt new file mode 100644 index 0000000..4568ddb --- /dev/null +++ b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/GetPath.kt @@ -0,0 +1,50 @@ +package org.example.project.ViewModel + +import android.net.Uri +import android.provider.OpenableColumns +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.runtime.* +import androidx.compose.ui.platform.LocalContext +import java.io.File +import java.io.FileOutputStream + +@Composable +actual fun GetPath(onPathSelected: (String) -> Unit) { + val context = LocalContext.current + var launched by remember { mutableStateOf(false) } + + val launcher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.GetContent(), + onResult = { uri: Uri? -> + if (uri != null) { + val inputStream = context.contentResolver.openInputStream(uri) + val fileName = getFileName(context.contentResolver, uri) ?: "temp_image.jpg" + val file = File(context.cacheDir, fileName) + val outputStream = FileOutputStream(file) + inputStream?.copyTo(outputStream) + inputStream?.close() + outputStream.close() + onPathSelected(file.absolutePath) + } + } + ) + + LaunchedEffect(Unit) { + if (!launched) { + launched = true + launcher.launch("image/*") + } + } +} + +fun getFileName(contentResolver: android.content.ContentResolver, uri: Uri): String? { + val returnCursor = contentResolver.query(uri, null, null, null, null) + returnCursor?.use { + val nameIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME) + if (it.moveToFirst()) { + return it.getString(nameIndex) + } + } + return null +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/IconPrinter.kt b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/IconPrinter.kt new file mode 100644 index 0000000..7eea79d --- /dev/null +++ b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/IconPrinter.kt @@ -0,0 +1,23 @@ +package org.example.project.ViewModel + +import androidx.compose.foundation.Image +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import org.example.project.R + +@Composable +actual fun IconPrinter(resourceName: String, modifier: Modifier) { + val resId = when (resourceName) { + "calendar" -> R.drawable.map + else -> return + } + + Image( + painter = painterResource(id = resId), + contentDescription = null, + modifier = modifier, + contentScale = ContentScale.FillBounds + ) +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/ImageFromPath.kt b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/ImageFromPath.kt new file mode 100644 index 0000000..137b335 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/ImageFromPath.kt @@ -0,0 +1,44 @@ +package org.example.project.ViewModel + +import android.graphics.BitmapFactory +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.imageResource +import org.example.project.R +import java.io.File + + +@Composable +actual fun ImageFromPath(path: String, modifier: Modifier) { + val imageBitmap: ImageBitmap? = when { + path.isEmpty() -> { + ImageBitmap.imageResource(id = R.drawable.basic_photo) + } + + else -> { + val file = File(path) + if (file.exists()) { + val bitmap = BitmapFactory.decodeFile(file.absolutePath) + bitmap?.asImageBitmap() + } else null + } + } + + if (imageBitmap != null) { + Image( + painter = BitmapPainter(imageBitmap), + contentDescription = null, + modifier = modifier, + contentScale = ContentScale.Crop + ) + } else { + Box(modifier = modifier.fillMaxSize()) + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/UserRepository.kt b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/UserRepository.kt new file mode 100644 index 0000000..fee7d8a --- /dev/null +++ b/composeApp/src/androidMain/kotlin/org/example/project/ViewModel/UserRepository.kt @@ -0,0 +1,69 @@ +package org.example.project.ViewModel + +import android.os.Build +import androidx.annotation.RequiresApi +import org.example.project.Models.Fond +import org.example.project.Models.User +import java.time.LocalDate + +actual class UserRepository { + @RequiresApi(Build.VERSION_CODES.O) + actual fun fetchUsers(): List { + var users = listOf( + User( + id = 1, + email = "email1@gmail.com", + password = "qwerty", + name = "bob", + surname = "bobby", + gender = "Мужской", + photopath = "/Users/rinchi/Desktop/vpn4ik.jpg", + LocalDate.of(2006, 1, 16), + country = "Russia", + active = false + ), + User( + id = 2, + email = "email2@gmail.com", + password = "qwerty", + name = "bob", + surname = "bobby", + gender = "Мужской", + photopath = "/Users/rinchi/Desktop/vpn4ik.jpg", + LocalDate.of(2006, 1, 16), + country = "Russia", + active = false + ), ) + return users + } + actual fun regUser(user: User): Boolean { + return true + } + actual fun fetchFonds(): List { + var fonds = listOf( + Fond( + id = 1, + name = "first", + balance = 1000 + ), + Fond( + id = 2, + name = "second", + balance = 2000 + ), + ) + return fonds + } + actual fun withdrawalBalance(idFond: Int, dedSum: Int): Boolean { + return true + } + actual fun getUserIdByEmail(email: String): Int { + return 0 + } + actual fun updateUserActive(idUser: Int): Boolean{ + return true + } + actual fun topUpBalance(idFond: Int, sum: Int): Boolean{ + return true + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/composeResources/drawable/defPhoto.png b/composeApp/src/commonMain/composeResources/drawable/defPhoto.png new file mode 100644 index 0000000..854f363 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/defPhoto.png differ diff --git a/composeApp/src/commonMain/composeResources/drawable/map.jpg b/composeApp/src/commonMain/composeResources/drawable/map.jpg new file mode 100644 index 0000000..dc4ae80 Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/map.jpg differ diff --git a/composeApp/src/commonMain/kotlin/org/example/project/Models/Fond.kt b/composeApp/src/commonMain/kotlin/org/example/project/Models/Fond.kt new file mode 100644 index 0000000..c89892b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/Models/Fond.kt @@ -0,0 +1,9 @@ +package org.example.project.Models + +import java.time.LocalDate + +data class Fond( + val id: Int, + val name: String, + val balance: Int +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/Models/User.kt b/composeApp/src/commonMain/kotlin/org/example/project/Models/User.kt new file mode 100644 index 0000000..7c0c847 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/Models/User.kt @@ -0,0 +1,17 @@ +package org.example.project.Models + +import java.time.LocalDate +import java.util.Date + +data class User( + val id: Int, + val email: String, + val password: String, + val name: String, + val surname: String, + val gender: String, + val photopath: String, + val birthday: LocalDate, + val country: String, + val active: Boolean +) diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/DatePickerField.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/DatePickerField.kt new file mode 100644 index 0000000..dbb604d --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/DatePickerField.kt @@ -0,0 +1,10 @@ +package org.example.project.ViewModel + +import androidx.compose.runtime.Composable + +@Composable +expect fun DatePickerField( + onDateSelected: (String) -> Unit, + trigger: Boolean, + onDismissRequest: () -> Unit +) diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/FieldWithDropdown.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/FieldWithDropdown.kt new file mode 100644 index 0000000..acd2645 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/FieldWithDropdown.kt @@ -0,0 +1,106 @@ +package org.example.project.ViewModel + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem +import androidx.compose.material.Icon +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDropDown +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.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp + +@Composable +fun FieldWithDropdown( + label: String, + options: List, + selectedOption: String, + onOptionSelected: (String) -> Unit, + fontSize: TextUnit, + labelWidth: Float +) { + var expanded by remember { mutableStateOf(false) } + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth() + ) { + Box(modifier = Modifier.weight(labelWidth)) { + Text( + text = label, + color = Color.DarkGray, + textAlign = TextAlign.Start, + fontSize = fontSize, + ) + } + + Box( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + .background(Color.White, shape = RoundedCornerShape(4.dp)) + .border( + width = 1.dp, + color = Color(0xFFCCCCCC), + shape = RoundedCornerShape(4.dp) + ) + .clickable { expanded = true } + .padding(12.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = selectedOption.ifEmpty { "..." }, + fontSize = fontSize, + color = if (selectedOption.isEmpty()) Color.LightGray else Color.Black + ) + + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = "Dropdown Arrow", + tint = Color.Gray + ) + } + + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier + .background(Color.White) + .fillMaxWidth() + ) { + options.forEach { option -> + DropdownMenuItem( + onClick = { + onOptionSelected(option) + expanded = false + } + ) { + Text(option, fontSize = fontSize, color = Color.Black) + } + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/FieldWithLabel.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/FieldWithLabel.kt new file mode 100644 index 0000000..d962fe1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/FieldWithLabel.kt @@ -0,0 +1,71 @@ +package org.example.project.ViewModel + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.TextUnit + +@Composable +fun FieldWithLabel( + label: String, + value: String, + onValueChange: (String) -> Unit, + placeholder: String, + fontSize: TextUnit, + labelWidth: Float, + enabled: Boolean = true, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth() + ) { + Box(modifier = Modifier.weight(labelWidth)) { + Text( + text = label, + color = Color.DarkGray, + textAlign = TextAlign.Start, + fontSize = fontSize, + modifier = Modifier + ) + } + + OutlinedTextField( + value = value, + onValueChange = onValueChange, + placeholder = { + if (placeholder.isNotEmpty()) { + Text( + text = placeholder, + fontSize = fontSize, + color = Color.LightGray + ) + } + }, + textStyle = androidx.compose.ui.text.TextStyle( + fontSize = fontSize, + color = Color.Black + ), + enabled = enabled, + colors = androidx.compose.material.TextFieldDefaults.outlinedTextFieldColors( + backgroundColor = Color.White, + cursorColor = Color.Black, + textColor = Color.Black, + placeholderColor = Color.LightGray, + focusedBorderColor = Color(0xFFCCCCCC), + unfocusedBorderColor = Color(0xFFCCCCCC), + disabledBorderColor = Color(0xFFCCCCCC) + ), + modifier = Modifier.weight(1f).wrapContentWidth().fillMaxWidth() + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/FieldWithLabelInt.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/FieldWithLabelInt.kt new file mode 100644 index 0000000..f224e49 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/FieldWithLabelInt.kt @@ -0,0 +1,85 @@ +package org.example.project.ViewModel + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.TextUnit + +@Composable +fun FieldWithLabelInt( + label: String, + value: Int, + onValueChange: (Int) -> Unit, + placeholder: String, + fontSize: TextUnit, + labelWidth: Float, + enabled: Boolean = true, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth() + ) { + Box(modifier = Modifier.weight(labelWidth)) { + Text( + text = label, + color = Color.DarkGray, + textAlign = TextAlign.Start, + fontSize = fontSize, + ) + } + + OutlinedTextField( + value = value.toString(), + onValueChange = { newValue -> + val intValue = newValue.toIntOrNull() + if (intValue != null) { + onValueChange(intValue) + } + }, + placeholder = { + if (placeholder.isNotEmpty()) { + Text( + text = placeholder, + fontSize = fontSize, + color = Color.LightGray + ) + } + }, + textStyle = androidx.compose.ui.text.TextStyle( + fontSize = fontSize, + color = Color.Black + ), + enabled = enabled, + colors = androidx.compose.material.TextFieldDefaults.outlinedTextFieldColors( + backgroundColor = Color.White, + cursorColor = Color.Black, + textColor = Color.Black, + placeholderColor = Color.LightGray, + focusedBorderColor = Color(0xFFCCCCCC), + unfocusedBorderColor = Color(0xFFCCCCCC), + disabledBorderColor = Color(0xFFCCCCCC) + ), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Done + ), + modifier = Modifier + .weight(1f) + .wrapContentWidth() + .fillMaxWidth() + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/GetPath.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/GetPath.kt new file mode 100644 index 0000000..efbcb70 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/GetPath.kt @@ -0,0 +1,7 @@ +package org.example.project.ViewModel + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +expect fun GetPath(onPathSelected: (String) -> Unit) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/IconPrinter.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/IconPrinter.kt new file mode 100644 index 0000000..1ab6505 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/IconPrinter.kt @@ -0,0 +1,7 @@ +package org.example.project.ViewModel + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +expect fun IconPrinter(resourceName: String, modifier: Modifier = Modifier) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/ImageFromPath.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/ImageFromPath.kt new file mode 100644 index 0000000..7ea323c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/ImageFromPath.kt @@ -0,0 +1,7 @@ +package org.example.project.ViewModel + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +expect fun ImageFromPath(path: String = "", modifier: Modifier = Modifier) diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/InfoButton.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/InfoButton.kt new file mode 100644 index 0000000..3d57283 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/InfoButton.kt @@ -0,0 +1,33 @@ +package org.example.project.ViewModel + +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp + + +@Composable +fun InfoButton(text: String, fontSize: TextUnit, onClick: () -> Unit) { + Button( + onClick = onClick, + modifier = Modifier + .fillMaxWidth() + .height(80.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp), + ) { + Text(text = text, fontStyle = FontStyle.Italic, fontSize = fontSize, textAlign = TextAlign.Center) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/RegRunnerViewModel.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/RegRunnerViewModel.kt new file mode 100644 index 0000000..80de433 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/RegRunnerViewModel.kt @@ -0,0 +1,18 @@ +package org.example.project.ViewModel + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel + +class RegRunnerViewModel : ViewModel() { + var email by mutableStateOf("") + var password by mutableStateOf("") + var secondPassword by mutableStateOf("") + var name by mutableStateOf("") + var surname by mutableStateOf("") + var gender by mutableStateOf("") + var country by mutableStateOf("") + var photoPath by mutableStateOf("") + var dateBirthday by mutableStateOf("") +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/TimerViewModel.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/TimerViewModel.kt index 5a9be4d..1fe6979 100644 --- a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/TimerViewModel.kt +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/TimerViewModel.kt @@ -6,16 +6,21 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import java.time.LocalDate +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoUnit +import java.util.Locale class TimerViewModel : ViewModel() { - private val _remainingTime = MutableStateFlow("18 дней 17 часов и 10 минут до старта марафона") - val remainingTime: StateFlow = _remainingTime - init { startTimer() } + private val _remainingTime = MutableStateFlow("18 дней 17 часов и 10 минут до старта марафона") + val remainingTime: StateFlow = _remainingTime + private fun startTimer() { viewModelScope.launch { var remainingMillis = (18 * 24 * 60 * 60 * 1000) + (17 * 60 * 60 * 1000) + (10 * 60 * 1000) diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/UserRepository.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/UserRepository.kt new file mode 100644 index 0000000..396d726 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/UserRepository.kt @@ -0,0 +1,14 @@ +package org.example.project.ViewModel + +import org.example.project.Models.Fond +import org.example.project.Models.User + +expect class UserRepository() { + fun fetchUsers(): List + fun regUser(user: User): Boolean + fun fetchFonds(): List + fun withdrawalBalance(idFond: Int, dedSum: Int): Boolean + fun getUserIdByEmail(email: String): Int + fun updateUserActive(idUser: Int): Boolean + fun topUpBalance(idFond: Int, sum: Int): Boolean +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/UserViewModel.kt b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/UserViewModel.kt new file mode 100644 index 0000000..3719460 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ViewModel/UserViewModel.kt @@ -0,0 +1,36 @@ +package org.example.project.ViewModel + +import org.example.project.Models.Fond +import org.example.project.Models.User + +class UserViewModel { + private val userRepository = UserRepository() + + fun getUsers(): List { + return userRepository.fetchUsers() + } + + fun regUser(user: User): Boolean { + return userRepository.regUser(user) + } + + fun getFond(): List{ + return userRepository.fetchFonds() + } + + fun withdrawalBalance(idFond: Int, dedSum: Int): Boolean{ + return userRepository.withdrawalBalance(idFond, dedSum) + } + + fun getUserIdByEmail(email: String): Int{ + return userRepository.getUserIdByEmail(email) + } + + fun updateUserActive(idUser: Int): Boolean{ + return userRepository.updateUserActive(idUser) + } + + fun topUpBalance(idFond: Int, sum: Int): Boolean{ + return userRepository.topUpBalance(idFond, sum) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/AboutMarathon.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/AboutMarathon.kt new file mode 100644 index 0000000..cf2553c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/AboutMarathon.kt @@ -0,0 +1,346 @@ +package org.example.project.ui.Screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DateRange +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +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.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import org.example.project.ViewModel.IconPrinter +import org.example.project.ViewModel.TimerViewModel + + +class AboutMarathonScreen(private val timerViewModel: TimerViewModel) : Screen { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + + Scaffold( + topBar = { AboutMarathonTopBar() }, + bottomBar = { AboutMarathonBottomBar(timerViewModel = timerViewModel) } + ) { paddingValues -> + + Box( + modifier = Modifier + .padding(paddingValues) + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + .padding(horizontal = 20.dp) + ) { + val fontSize = (maxWidth.value / 25).sp + val fontSizeSecond = (maxWidth.value / 35).sp + + Column( + modifier = Modifier + .padding(16.dp) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween + ) { + Spacer(modifier = Modifier.weight(0.1f)) + + Text( + text = "Информация о Marathon Skills 2016", + color = Color.Black, + textAlign = TextAlign.Center, + fontSize = fontSize, + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + Row( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + Column( + modifier = Modifier + .weight(0.6f) + .padding(end = 8.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Box( + modifier = Modifier + .aspectRatio(3f) + .fillMaxWidth() + .border(width = 2.dp, color = Color.Black, shape = RoundedCornerShape(15.dp)) + .clip(RoundedCornerShape(15.dp)) + .background(Color.LightGray) + .clickable { }, + contentAlignment = Alignment.Center + ) { + IconPrinter( + resourceName = "map", + modifier = Modifier.fillMaxSize() + ) + } + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { + Box( + modifier = Modifier + .weight(1f) + .aspectRatio(1.7f) + .border(2.dp, Color.Black, RoundedCornerShape(15.dp)) + .clip(RoundedCornerShape(15.dp)) + .background(Color.LightGray) + .clickable { }, + contentAlignment = Alignment.Center + ) { + IconPrinter( + resourceName = "map", + modifier = Modifier.fillMaxSize() + ) + } + + Box( + modifier = Modifier + .weight(1f) + .aspectRatio(1.7f) + .border(2.dp, Color.Black, RoundedCornerShape(15.dp)) + .clip(RoundedCornerShape(15.dp)) + .background(Color.LightGray) + .clickable { }, + contentAlignment = Alignment.Center + ) { + IconPrinter( + resourceName = "map", + modifier = Modifier.fillMaxSize() + ) + } + } + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { + Box( + modifier = Modifier + .weight(1f) + .aspectRatio(1.7f) + .border(2.dp, Color.Black, RoundedCornerShape(15.dp)) + .clip(RoundedCornerShape(15.dp)) + .background(Color.LightGray) + .clickable { }, + contentAlignment = Alignment.Center + ) { + IconPrinter( + resourceName = "map", + modifier = Modifier.fillMaxSize() + ) + } + + Box( + modifier = Modifier + .weight(1f) + .aspectRatio(1.7f) + .border(2.dp, Color.Black, RoundedCornerShape(15.dp)) + .clip(RoundedCornerShape(15.dp)) + .background(Color.LightGray) + .clickable { }, + contentAlignment = Alignment.Center + ) { + IconPrinter( + resourceName = "map", + modifier = Modifier.fillMaxSize() + ) + } + } + } + + Column( + modifier = Modifier + .weight(1f) + .padding(start = 8.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Информация о Marathon Skills 2016", + color = Color.Black, + textAlign = TextAlign.Center, + fontSize = fontSizeSecond, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = Modifier.weight(0.3f)) + + Text( + text = "Информация о Marathon Skills 2016", + color = Color.Black, + textAlign = TextAlign.Center, + fontSize = fontSizeSecond, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = Modifier.weight(0.3f)) + + Text( + text = "Информация о Marathon Skills 2016", + color = Color.Black, + textAlign = TextAlign.Center, + fontSize = fontSizeSecond, + modifier = Modifier.weight(1f) + ) + + Spacer(modifier = Modifier.weight(0.3f)) + } + } + } + } + } + } + } + + @Composable + private fun AboutMarathonTopBar() { + val navigator = LocalNavigator.currentOrThrow + + Box { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + ) { + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { navigator.push(MainScreen()) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + + Spacer(modifier = Modifier.width(64.dp)) + + Button( + onClick = { navigator.push(MainScreen()) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Logout", + color = Color.Black, + fontSize = fontSizeButton + ) + } + } + } + } + } + + @Composable + private fun AboutMarathonBottomBar(timerViewModel: TimerViewModel) { + val remainingTime = timerViewModel.remainingTime.collectAsState().value + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp + + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = fontSize + ) + } + } + } + } +} diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ConfirmRunner.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ConfirmRunner.kt new file mode 100644 index 0000000..9fae2ea --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ConfirmRunner.kt @@ -0,0 +1,217 @@ +package org.example.project.ui.Screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import org.example.project.ViewModel.TimerViewModel + +class ConfirmRunnerScreen(private val timerViewModel: TimerViewModel, private val idRunner: Int) : + Screen { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + + Scaffold( + topBar = { ConfirmRunnerTopBar() }, + bottomBar = { ConfirmRunnerBottomBar(timerViewModel = timerViewModel) } + ) { paddingValues -> + + Box( + modifier = Modifier.padding(paddingValues) + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + .padding(horizontal = 100.dp) + ) { + val fontSizeFirst = (maxWidth.value / 30).sp + val fontSizeSecond = (maxWidth.value / 40).sp + + Column( + modifier = Modifier + .fillMaxWidth().padding(bottom = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(modifier = Modifier.weight(0.2f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Спасибо за вашу регистрацию в качестве бегуна!", + color = Color.Gray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.2f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Спасибо за вашу регистрацию в качестве бегуна в Marathon Skills 2016!\n С вами свяжутся по поводу оплаты.", + color = Color.Black, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.2f)) + + Button( + onClick = { navigator.push(InfoRunnerScreen(timerViewModel, idRunner)) }, + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Ок", fontSize = fontSizeSecond) + } + + Spacer(modifier = Modifier.weight(1f)) + } + } + } + } + } + + @Composable + private fun ConfirmRunnerTopBar() { + val navigator = LocalNavigator.currentOrThrow + + Box { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + ) { + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + + Spacer(modifier = Modifier.width(64.dp)) + + Button( + onClick = { navigator.push(MainScreen()) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Logout", + color = Color.Black, + fontSize = fontSizeButton + ) + } + } + } + } + } + + + @Composable + private fun ConfirmRunnerBottomBar(timerViewModel: TimerViewModel) { + val remainingTime = timerViewModel.remainingTime.collectAsState().value + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp + + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = fontSize + ) + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ConfirmSponsor.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ConfirmSponsor.kt new file mode 100644 index 0000000..2fc5556 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ConfirmSponsor.kt @@ -0,0 +1,234 @@ +package org.example.project.ui.Screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import org.example.project.ViewModel.TimerViewModel + +class ConfirmSponsorScreen(private val timerViewModel: TimerViewModel, private val sumSpons: String, private val runner: String, private val blagot: String) : + Screen { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + + Scaffold( + topBar = { ConfirmSponsorTopBar() }, + bottomBar = { ConfirmSponsorBottomBar(timerViewModel = timerViewModel) } + ) { paddingValues -> + + Box( + modifier = Modifier.padding(paddingValues) + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + .padding(horizontal = 100.dp) + ) { + val fontSize = (maxWidth.value / 60).sp + val fontSizeFirst = (maxWidth.value / 30).sp + val fontSizeSecond = (maxWidth.value / 40).sp + val fontSizeThird = (maxWidth.value / 50).sp + val fontSizeFourth = (maxWidth.value / 10).sp + val fontSizeFiveth = (maxWidth.value / 20).sp + + Column( + modifier = Modifier + .fillMaxWidth().padding(bottom = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Спасибо за вашу спонсорскую поддержку!", + color = Color.Gray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Спасибо за поддержку бегуна в Marathon Skills 2016! Ваше пожертвование пойдёт в его благотворительную организацию", + color = Color.Black, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = runner, + color = Color.Black, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = blagot, + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFiveth, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = sumSpons, + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFourth, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Button( + onClick = { navigator.push(MainScreen()) }, + modifier = Modifier + .fillMaxWidth() + .height(48.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Назад", fontSize = fontSizeSecond) + } + } + } + } + } + } + + @Composable + private fun ConfirmSponsorTopBar() { + val navigator = LocalNavigator.currentOrThrow + + Box { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + ) { + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + } + } + } + } + + + + @Composable + private fun ConfirmSponsorBottomBar(timerViewModel: TimerViewModel) { + val remainingTime = timerViewModel.remainingTime.collectAsState().value + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp + + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = fontSize + ) + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ForRunner.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ForRunner.kt index 9873b18..68d5bb1 100644 --- a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ForRunner.kt +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ForRunner.kt @@ -23,71 +23,88 @@ import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.compose.viewModel +import org.example.project.ViewModel.RegRunnerViewModel import org.example.project.ViewModel.TimerViewModel class RunnerScreen(private val timerViewModel: TimerViewModel) : Screen { @Composable override fun Content() { - - val buttonSize = Modifier - .width(400.dp) - .height(80.dp) + val navigator = LocalNavigator.currentOrThrow Scaffold( topBar = { RunnerTopBar() }, bottomBar = { RunnerBottomBar(timerViewModel = timerViewModel) } ) { paddingValues -> + Box( modifier = Modifier .padding(paddingValues) - .fillMaxSize() ) { - Column( + BoxWithConstraints( modifier = Modifier .fillMaxWidth() .align(Alignment.Center) - .padding(horizontal = 30.dp), - horizontalAlignment = Alignment.CenterHorizontally, + .padding(horizontal = 100.dp) ) { - Button( - onClick = { }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) - ) { - Text("Я участвовал ранее", - fontSize = 16.sp - ) - } + val fontSize = (maxWidth.value / 25).sp - Spacer(modifier = Modifier.height(16.dp)) - - Button( - onClick = { }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) + Column( + modifier = Modifier + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, ) { - Text("Я новый участник", - fontSize = 16.sp - ) + Spacer(Modifier.weight(1f)) + + Button( + onClick = { navigator.push(LoginScreen(timerViewModel)) }, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text( + "Я участвовал ранее", + fontSize = fontSize + ) + } + + Spacer(modifier = Modifier.weight(0.25f)) + + Button( + onClick = { navigator.push(RegRunnerScreen(timerViewModel)) }, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text( + "Я новый участник", + fontSize = fontSize + ) + } + + Spacer(Modifier.weight(1f)) } } - Button( - onClick = { }, + onClick = { navigator.push(LoginScreen(timerViewModel)) }, modifier = Modifier .align(Alignment.BottomEnd) .height(65.dp) @@ -108,61 +125,85 @@ class RunnerScreen(private val timerViewModel: TimerViewModel) : Screen { private fun RunnerTopBar() { val navigator = LocalNavigator.currentOrThrow - Row( - modifier = Modifier - .fillMaxWidth() - .background(Color.DarkGray) - .padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - - Button( - onClick = { navigator.pop() }, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - elevation = null, - shape = RoundedCornerShape(15.dp), + Box { + BoxWithConstraints( modifier = Modifier - .wrapContentWidth() - + .fillMaxWidth() + .align(Alignment.Center) ) { - Text(text = "Назад", - color = Color.Black, - ) + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { navigator.pop() }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + + Spacer(modifier = Modifier.width(64.dp)) + } } - - Spacer(modifier = Modifier.width(24.dp)) - - Text( - text = "MARATHON SKILLS 2016", - style = MaterialTheme.typography.h6, - modifier = Modifier.weight(1f), - textAlign = TextAlign.Left, - fontWeight = FontWeight.Bold, - fontSize = MaterialTheme.typography.h4.fontSize, - color = Color.White - ) - - Spacer(modifier = Modifier.width(64.dp)) } } @Composable private fun RunnerBottomBar(timerViewModel: TimerViewModel) { val remainingTime = timerViewModel.remainingTime.collectAsState().value + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp - Column( - modifier = Modifier - .fillMaxWidth() - .background(Color.DarkGray) - .padding(8.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(text = remainingTime, - color = Color.White) + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = fontSize + ) + } + } } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ForSponsor.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ForSponsor.kt new file mode 100644 index 0000000..34efaee --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/ForSponsor.kt @@ -0,0 +1,466 @@ +package org.example.project.ui.Screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import org.example.project.ViewModel.FieldWithDropdown +import org.example.project.ViewModel.FieldWithLabel +import org.example.project.ViewModel.TimerViewModel +import org.example.project.ViewModel.UserViewModel + +class ForSponsorScreen(private val timerViewModel: TimerViewModel) : Screen { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + val viewModel = UserViewModel() + val fonds = viewModel.getFond() + val runners = viewModel.getUsers() + + Scaffold( + topBar = { ForSponsorTopBar() }, + bottomBar = { ForSposnorBottomBar(timerViewModel = timerViewModel) } + ) { paddingValues -> + + Box( + modifier = Modifier.padding(paddingValues) + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + .padding(horizontal = 100.dp) + ) { + val fontSizeFirst = (maxWidth.value / 30).sp + val fontSizeSecond = (maxWidth.value / 40).sp + val fontSizeThird = (maxWidth.value / 50).sp + val fontSizeFourth = (maxWidth.value / 10).sp + val labelWidth = 0.5f + var fondsNames = fonds.map { it.name } + var runnersNames = runners.map { it.name } + var selectedFondName by remember { mutableStateOf("") } + var fio by remember { mutableStateOf("") } + var runner by remember { mutableStateOf("") } + var cardName by remember { mutableStateOf("") } + var cardNum by remember { mutableStateOf("") } + var srokM by remember { mutableStateOf("") } + var srokY by remember { mutableStateOf("") } + var cvc by remember { mutableStateOf("") } + var sumChangStr by remember { mutableStateOf("10")} + var sumChang by remember { mutableStateOf(sumChangStr.toInt()) } + var sumSpons by remember { mutableStateOf(0)} + + Column( + modifier = Modifier + .fillMaxWidth().padding(bottom = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = "Спонсор бегуна", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(Modifier.weight(0.1f)) + + Text( + text = "Пожалуйста выберите бегуна, которого вы хотели бы спонсировать и сумму, которую вы хотели бы спонсировать. Спасибо за вашу поддержку бегунов и их благотворительных учреждений.", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSizeSecond, + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(Modifier.weight(0.2f)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Column(modifier = Modifier.weight(1f),) { + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Информация о спонсоре", + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabel( + label = "Ваше имя:", + value = fio, + onValueChange = { fio = it }, + placeholder = "Ваше имя", + fontSize = fontSizeSecond, + labelWidth = labelWidth, + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithDropdown( + label = "Бегун:", + options = runnersNames, + selectedOption = runner, + onOptionSelected = { runner = it }, + fontSize = fontSizeSecond, + labelWidth = labelWidth + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabel( + label = "Карта:", + value = cardName, + onValueChange = { cardName = it }, + placeholder = "Владелец карты", + fontSize = fontSizeSecond, + labelWidth = labelWidth, + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabel( + label = "Номер карты#:", + value = cardNum, + onValueChange = { cardNum = it }, + placeholder = "...", + fontSize = fontSizeSecond, + labelWidth = labelWidth, + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = "Срок действия:", + fontSize = fontSizeSecond, + color = Color.Black, + modifier = Modifier.weight(labelWidth) + ) + + OutlinedTextField( + value = srokM, + onValueChange = { srokM = it }, + placeholder = { Text("MM", fontSize = fontSizeSecond) }, + textStyle = androidx.compose.ui.text.TextStyle( + fontSize = fontSizeSecond, + color = Color.Black + ), + modifier = Modifier + .weight((1f - labelWidth) / 2) + .padding(end = 4.dp), + singleLine = true + ) + + OutlinedTextField( + value = srokY, + onValueChange = { srokY = it }, + placeholder = { Text("YY", fontSize = fontSizeSecond) }, + textStyle = androidx.compose.ui.text.TextStyle( + fontSize = fontSizeSecond, + color = Color.Black + ), + modifier = Modifier + .weight((1f - labelWidth) / 2) + .padding(start = 4.dp), + singleLine = true + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabel( + label = "CVC:", + value = cvc, + onValueChange = { cvc = it }, + placeholder = "...", + fontSize = fontSizeSecond, + labelWidth = labelWidth, + ) + } + + Column(modifier = Modifier.weight(1f),) { + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Благотворительность", + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithDropdown( + label = "", + options = fondsNames, + selectedOption = selectedFondName, + onOptionSelected = { selectedFondName = it }, + fontSize = fontSizeSecond, + labelWidth = 0.5f + ) + + Spacer(modifier = Modifier.weight(0.5f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Сумма пожертвования", + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "$" + sumSpons.toString(), + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFourth, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + Button( + modifier = Modifier + .weight(0.2f) + .aspectRatio(1f), + onClick = { + sumSpons -= sumChang + }, + shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + ) { + Text("-", fontSize = fontSizeSecond) + } + + OutlinedTextField( + value = sumChangStr, + onValueChange = { + sumChangStr = it + sumChang = it.toIntOrNull() ?: 0 + }, + placeholder = { Text("...", fontSize = fontSizeSecond) }, + textStyle = androidx.compose.ui.text.TextStyle( + fontSize = fontSizeSecond, + color = Color.Black, + textAlign = TextAlign.Center + ), + modifier = Modifier + .weight(0.6f) + .padding(horizontal = 4.dp), + singleLine = true + ) + + Button( + modifier = Modifier + .weight(0.2f) + .aspectRatio(1f), + onClick = { + sumSpons += sumChang + }, + shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + ) { + Text("+", fontSize = fontSizeSecond) + } + } + + Spacer(modifier = Modifier.weight(0.5f)) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { + Button( + onClick = { + val selectedFond = fonds.find { it.name == selectedFondName } + if (selectedFond != null) { + val result = viewModel.topUpBalance(selectedFond.id, sumSpons) + if (result){ + navigator.push(ConfirmSponsorScreen(timerViewModel, sumSpons.toString(), runner, selectedFondName)) + } + } + }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Заплатить", fontSize = fontSizeThird) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Button( + onClick = { navigator.push(MainScreen()) }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Отмена", fontSize = fontSizeThird) + } + } + } + } + } + } + } + } + } + + @Composable + private fun ForSponsorTopBar() { + val navigator = LocalNavigator.currentOrThrow + + Box { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + ) { + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { navigator.push(MainScreen()) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + } + } + } + } + + + + @Composable + private fun ForSposnorBottomBar(timerViewModel: TimerViewModel) { + val remainingTime = timerViewModel.remainingTime.collectAsState().value + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp + + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = fontSize + ) + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/InfoRunner.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/InfoRunner.kt new file mode 100644 index 0000000..13305c2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/InfoRunner.kt @@ -0,0 +1,215 @@ +package org.example.project.ui.Screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +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.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import org.example.project.ViewModel.InfoButton +import org.example.project.ViewModel.TimerViewModel + + +class InfoRunnerScreen(private val timerViewModel: TimerViewModel, private val idRunner: Int) : Screen { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + + Scaffold( + topBar = { InfoRunnerTopBar() }, + bottomBar = { InfoRunnerBottomBar(timerViewModel = timerViewModel) } + ) { paddingValues -> + + Box( + modifier = Modifier + .padding(paddingValues) + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + .padding(horizontal = 100.dp) + ) { + val fontSize = (maxWidth.value / 25).sp + val fontSizeButton = (maxWidth.value / 40).sp + + Column( + modifier = Modifier + .padding(16.dp) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "Подробная информация", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSize, + ) + + Row( + modifier = Modifier + .fillMaxWidth() + .weight(1f), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically + ) { + Column( + modifier = Modifier + .weight(1f) + .padding(end = 8.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + InfoButton("Регистрация на марафон", fontSizeButton, {navigator.push(RegMaraphonScreen(timerViewModel, idRunner))}) + InfoButton("Редактирование профиля", fontSizeButton, {}) + InfoButton("Контакты", fontSizeButton, {}) + } + + Column( + modifier = Modifier + .weight(1f) + .padding(start = 8.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + InfoButton("Мои результаты", fontSizeButton, {}) + InfoButton("Мои спонсоры", fontSizeButton, {}) + } + } + } + } + } + } + } + + @Composable + private fun InfoRunnerTopBar() { + val navigator = LocalNavigator.currentOrThrow + + Box { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + ) { + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { navigator.push(MainScreen()) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + + Spacer(modifier = Modifier.width(64.dp)) + + Button( + onClick = { navigator.push(MainScreen()) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Logout", + color = Color.Black, + fontSize = fontSizeButton + ) + } + } + } + } + } + + @Composable + private fun InfoRunnerBottomBar(timerViewModel: TimerViewModel) { + val remainingTime = timerViewModel.remainingTime.collectAsState().value + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp + + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = fontSize + ) + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/Login.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/Login.kt new file mode 100644 index 0000000..2f0faa1 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/Login.kt @@ -0,0 +1,258 @@ +package org.example.project.ui.Screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.ui.Modifier +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import org.example.project.ViewModel.FieldWithLabel +import org.example.project.ViewModel.TimerViewModel +import org.example.project.ViewModel.UserViewModel + +class LoginScreen(private val timerViewModel: TimerViewModel) : Screen { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + + Scaffold( + topBar = { LoginTopBar() }, + bottomBar = { LoginBottomBar(timerViewModel = timerViewModel) } + ) { paddingValues -> + + val viewModel = UserViewModel() + val users = viewModel.getUsers() + + Box( + modifier = Modifier.padding(paddingValues) + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + .padding(horizontal = 100.dp) + ) { + val fontSize = (maxWidth.value / 25).sp + val fontSizeFirst = (maxWidth.value / 20).sp + val fontSizeSecond = (maxWidth.value / 35).sp + val labelWidth = 0.25f + var email by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + + Column( + modifier = Modifier + .fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(Modifier.weight(0.25f)) + + Text( + text = "Форма авторизации", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(Modifier.weight(0.25f)) + + Text( + text = "Пожалуйста, авторизуйтесь в системе, используя\n ваш адрес электронной почты и пароль", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSizeSecond, + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(Modifier.weight(0.25f)) + + FieldWithLabel( + label = "Email:", + value = email, + onValueChange = { email = it }, + placeholder = "Enter your email address", + fontSize = fontSize, + labelWidth = labelWidth, + ) + + Spacer(Modifier.weight(0.25f)) + + FieldWithLabel( + label = "Password:", + value = password, + onValueChange = { password = it }, + placeholder = "Enter your password", + fontSize = fontSize, + labelWidth = labelWidth, + ) + + Spacer(Modifier.weight(0.25f)) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Button( + onClick = { + val user = users.find { it.email == email } + if (user != null && user.password == password) { + val userId = viewModel.getUserIdByEmail(user.email) + navigator.push(InfoRunnerScreen(timerViewModel, userId)) + } + }, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text( + "Login", + fontSize = fontSize + ) + } + + Spacer(Modifier.weight(0.25f)) + + Button( + onClick = { navigator.push(MainScreen()) }, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text( + "Cancel", + fontSize = fontSize + ) + } + } + + Spacer(Modifier.weight(1f)) + } + } + } + } + } + + @Composable + private fun LoginTopBar() { + val navigator = LocalNavigator.currentOrThrow + + Box { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + ) { + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { navigator.push(MainScreen()) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + + Spacer(modifier = Modifier.width(64.dp)) + } + } + } + } + + @Composable + private fun LoginBottomBar(timerViewModel: TimerViewModel) { + val remainingTime = timerViewModel.remainingTime.collectAsState().value + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp + + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = fontSize + ) + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/MainScreen.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/MainScreen.kt index a27779c..8ee4551 100644 --- a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/MainScreen.kt +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/MainScreen.kt @@ -17,7 +17,6 @@ import androidx.compose.ui.unit.sp import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow -import kotlinx.coroutines.delay class MainScreen : Screen { @@ -26,10 +25,6 @@ class MainScreen : Screen { val navigator = LocalNavigator.currentOrThrow val timerViewModel = TimerViewModel() - val buttonSize = Modifier - .width(400.dp) - .height(80.dp) - Scaffold( topBar = { MainWindowTopBar() }, bottomBar = { MainWindowBottomBar(timerViewModel) } @@ -37,65 +32,73 @@ class MainScreen : Screen { Box( modifier = Modifier .padding(paddingValues) - .fillMaxSize() ) { - Column( + BoxWithConstraints( modifier = Modifier .fillMaxWidth() .align(Alignment.Center) - .padding(horizontal = 30.dp), - horizontalAlignment = Alignment.CenterHorizontally, + .padding(horizontal = 100.dp) ) { - Button( - onClick = { navigator.push(RunnerScreen(timerViewModel)) }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) + val fontSize = (maxWidth.value / 25).sp + + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally ) { - Text("Я хочу стать бегуном", - fontSize = 16.sp - ) - } + Spacer(modifier = Modifier.weight(0.25f)) - Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = { navigator.push(RunnerScreen(timerViewModel)) }, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Я хочу стать бегуном", fontSize = fontSize) + } - Button( - onClick = { }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) - ) { - Text("Я хочу стать спонсором бегуна", - fontSize = 16.sp - ) - } + Spacer(modifier = Modifier.height(16.dp)) - Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = { navigator.push(ForSponsorScreen(timerViewModel))}, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Я хочу стать спонсором бегуна", fontSize = fontSize) + } - Button( - onClick = { navigator.push(MoreInfoScreen(timerViewModel)) }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) - ) { - Text(text = "Я хочу узнать больше о событии", - fontSize = 16.sp - ) + Spacer(modifier = Modifier.height(16.dp)) + + Button( + onClick = { navigator.push(MoreInfoScreen(timerViewModel)) }, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Я хочу узнать больше о событии", fontSize = fontSize) + } + + Spacer(modifier = Modifier.weight(0.5f)) } } - Button( - onClick = { }, + onClick = { navigator.push(LoginScreen(timerViewModel)) }, modifier = Modifier .align(Alignment.BottomEnd) .height(65.dp) @@ -106,7 +109,7 @@ class MainScreen : Screen { ), shape = RoundedCornerShape(15.dp) ) { - Text(text = "Login", color = Color.Black) + Text("Login") } } } @@ -114,29 +117,39 @@ class MainScreen : Screen { @Composable private fun MainWindowTopBar() { - Column( - modifier = Modifier - .fillMaxWidth() - .background(Color.DarkGray) - .padding(32.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = "MARATHON SKILLS 2016", - fontSize = MaterialTheme.typography.h4.fontSize, - fontWeight = FontWeight.Bold, - color = Color.White - ) + Box { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 25).sp - Spacer(modifier = Modifier.height(20.dp)) + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(32.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "MARATHON SKILLS 2016", + fontSize = fontSize, + fontWeight = FontWeight.Bold, + color = Color.White + ) - Text( - text = "Москва, Россия\nсреда 21 октября 2016", - textAlign = TextAlign.Center, - fontSize = MaterialTheme.typography.subtitle1.fontSize, - fontStyle = FontStyle.Italic, - color = Color.LightGray - ) + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = "Москва, Россия\nсреда 21 октября 2016", + textAlign = TextAlign.Center, + fontSize = fontSize, + fontStyle = FontStyle.Italic, + color = Color.LightGray + ) + } + } } } @@ -144,15 +157,25 @@ class MainScreen : Screen { private fun MainWindowBottomBar(timerViewModel: TimerViewModel) { val remainingTime = timerViewModel.remainingTime.collectAsState().value - Column( - modifier = Modifier - .fillMaxWidth() - .background(Color.DarkGray) - .padding(8.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(text = remainingTime, - color = Color.White) + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp + + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text(text = remainingTime, + color = Color.White, + fontSize = fontSize) + } + } } } } diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/MoreInfo.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/MoreInfo.kt index 273a457..15ad302 100644 --- a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/MoreInfo.kt +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/MoreInfo.kt @@ -3,12 +3,12 @@ package org.example.project.ui.Screens import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints 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.width import androidx.compose.foundation.layout.wrapContentWidth @@ -23,7 +23,6 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp @@ -31,6 +30,7 @@ import androidx.compose.ui.unit.sp import cafe.adriel.voyager.core.screen.Screen import cafe.adriel.voyager.navigator.LocalNavigator import cafe.adriel.voyager.navigator.currentOrThrow +import org.example.project.ViewModel.InfoButton import org.example.project.ViewModel.TimerViewModel @@ -38,133 +38,69 @@ class MoreInfoScreen(private val timerViewModel: TimerViewModel) : Screen { @Composable override fun Content() { - - val buttonSize = Modifier - .width(300.dp) - .height(80.dp) + val navigator = LocalNavigator.currentOrThrow Scaffold( topBar = { InfoTopBar() }, bottomBar = { InfoBottomBar(timerViewModel = timerViewModel) } ) { paddingValues -> + Box( modifier = Modifier .padding(paddingValues) - .fillMaxSize() ) { - Column( + BoxWithConstraints( modifier = Modifier - .padding(16.dp) - .fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceBetween + .fillMaxWidth() + .align(Alignment.Center) + .padding(horizontal = 100.dp) ) { - Text( - text = "Подробная информация", - color = Color.DarkGray, - textAlign = TextAlign.Center, - fontSize = MaterialTheme.typography.h5.fontSize, - ) + val fontSize = (maxWidth.value / 25).sp + val fontSizeButton = (maxWidth.value / 40).sp - Row( + Column( modifier = Modifier - .fillMaxWidth() - .weight(1f), - horizontalArrangement = Arrangement.SpaceEvenly, - verticalAlignment = Alignment.CenterVertically + .padding(16.dp) + .fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween ) { - Column( + Text( + text = "Подробная информация", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSize, + ) + + Row( modifier = Modifier - .weight(1f) - .padding(end = 8.dp), - verticalArrangement = Arrangement.spacedBy(16.dp), - horizontalAlignment = Alignment.CenterHorizontally + .fillMaxWidth() + .weight(1f), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically ) { - Button( - onClick = { }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) + Column( + modifier = Modifier + .weight(1f) + .padding(end = 8.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { - Text(text = "Marathon Skills 2016", fontSize = 16.sp, - fontStyle = FontStyle.Italic - ) + InfoButton("Marathon Skills 2016", fontSizeButton, {navigator.push(AboutMarathonScreen(timerViewModel))}) + InfoButton("Предыдущие результаты", fontSizeButton, {}) + InfoButton("BMI калькулятор", fontSizeButton, {}) } - Button( - onClick = { }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) + Column( + modifier = Modifier + .weight(1f) + .padding(start = 8.dp), + verticalArrangement = Arrangement.spacedBy(16.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { - Text(text = "Предыдущие\nрезультаты", fontSize = 16.sp, - fontStyle = FontStyle.Italic) - } - - Button( - onClick = { }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) - ) { - Text(text = "BMI калькулятор", fontSize = 16.sp, - fontStyle = FontStyle.Italic) - } - } - - Column( - modifier = Modifier - .weight(1f) - .padding(end = 8.dp), - verticalArrangement = Arrangement.spacedBy(16.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Button( - onClick = { }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) - ) { - Text(text = "Насколько долгий\nмарафон", fontSize = 16.sp, - fontStyle = FontStyle.Italic) - } - - Button( - onClick = { }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) - ) { - Text(text = "Список\nблаготворительных\nорганизаций", fontSize = 16.sp, - fontStyle = FontStyle.Italic) - } - - Button( - onClick = { }, - modifier = buttonSize, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - shape = RoundedCornerShape(15.dp) - ) { - Text(text = "BMR калькулятор", fontSize = 16.sp, - fontStyle = FontStyle.Italic) + InfoButton("Насколько долгий марафон", fontSizeButton, {}) + InfoButton("Список\nблаготворительных организаций", fontSizeButton, {}) + InfoButton("BMR калькулятор", fontSizeButton, {}) } } } @@ -177,61 +113,85 @@ class MoreInfoScreen(private val timerViewModel: TimerViewModel) : Screen { private fun InfoTopBar() { val navigator = LocalNavigator.currentOrThrow - Row( - modifier = Modifier - .fillMaxWidth() - .background(Color.DarkGray) - .padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - - Button( - onClick = { navigator.pop() }, - colors = ButtonDefaults.buttonColors( - backgroundColor = Color.LightGray, - contentColor = Color.Black - ), - elevation = null, - shape = RoundedCornerShape(15.dp), + Box { + BoxWithConstraints( modifier = Modifier - .wrapContentWidth() - + .fillMaxWidth() + .align(Alignment.Center) ) { - Text(text = "Назад", - color = Color.Black, - ) + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { navigator.pop() }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + + Spacer(modifier = Modifier.width(64.dp)) + } } - - Spacer(modifier = Modifier.width(24.dp)) - - Text( - text = "MARATHON SKILLS 2016", - style = MaterialTheme.typography.h6, - modifier = Modifier.weight(1f), - textAlign = TextAlign.Left, - fontWeight = FontWeight.Bold, - fontSize = MaterialTheme.typography.h4.fontSize, - color = Color.White - ) - - Spacer(modifier = Modifier.width(64.dp)) } } @Composable private fun InfoBottomBar(timerViewModel: TimerViewModel) { val remainingTime = timerViewModel.remainingTime.collectAsState().value + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp - Column( - modifier = Modifier - .fillMaxWidth() - .background(Color.DarkGray) - .padding(8.dp), - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text(text = remainingTime, - color = Color.White) + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = fontSize + ) + } + } } } } \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/RegMaraphon.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/RegMaraphon.kt new file mode 100644 index 0000000..f6495ec --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/RegMaraphon.kt @@ -0,0 +1,422 @@ +package org.example.project.ui.Screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Checkbox +import androidx.compose.material.MaterialTheme +import androidx.compose.material.RadioButton +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import org.example.project.ViewModel.FieldWithDropdown +import org.example.project.ViewModel.FieldWithLabelInt +import org.example.project.ViewModel.TimerViewModel +import org.example.project.ViewModel.UserViewModel + +class RegMaraphonScreen(private val timerViewModel: TimerViewModel, private val userId: Int) : Screen { + + @Composable + override fun Content() { + val navigator = LocalNavigator.currentOrThrow + val viewModel = UserViewModel() + val fonds = viewModel.getFond() + + Scaffold( + topBar = { RegMaraphonTopBar() }, + bottomBar = { RegMaraphonBottomBar(timerViewModel = timerViewModel) } + ) { paddingValues -> + + Box( + modifier = Modifier.padding(paddingValues) + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + .padding(horizontal = 100.dp) + ) { + val fontSizeFirst = (maxWidth.value / 30).sp + val fontSizeSecond = (maxWidth.value / 40).sp + val fontSizeThird = (maxWidth.value / 50).sp + val fontSizeFourth = (maxWidth.value / 10).sp + var finalSum by remember { mutableStateOf(0) } + var previousAddonPrice by remember { mutableStateOf(0) } + var checked1 by remember { mutableStateOf(false) } + var checked2 by remember { mutableStateOf(false) } + var checked3 by remember { mutableStateOf(false) } + val options = listOf( + "Вариант А($0): Номер бегуна + RFID браслет", + "Вариант B($20): вариант A + бейсболка + бутылка воды", + "Вариант C($45): Вариант B + футболка + сувенирный буклет" + ) + var fondsNames = fonds.map { it.name } + var selectedOption by remember { mutableStateOf(options[0]) } + var vznos by remember { mutableStateOf("")} + + Column( + modifier = Modifier + .fillMaxWidth().padding(bottom = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = "Регистрация на марафон", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(Modifier.weight(0.1f)) + + Text( + text = "Пожалуйста, заполните всю информацию, чтобы зарегестрироваться на Skills Marathon 2016 проводимом в Москве. Russia. С вами свяжутся после регистрации для уточнения оплаты и другой информации.", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSizeSecond, + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(Modifier.weight(0.2f)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Column(modifier = Modifier.weight(1f), ) { + Spacer(modifier = Modifier.weight(0.1f)) + + Text( + text = "Вид марафона", + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier + ) + + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = checked1, + onCheckedChange = { isChecked -> + checked1 = isChecked + if (isChecked) { + finalSum += 145 + } else { + finalSum -= 145 + } + } + ) + Spacer(modifier = Modifier.width(8.dp)) + Text(text = "42km Полный марафон($145)", fontSize = fontSizeSecond) + } + + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = checked2, + onCheckedChange = { isChecked -> + checked2 = isChecked + if (isChecked) { + finalSum += 75 + } else { + finalSum -= 75 + } + } + ) + Spacer(modifier = Modifier.width(8.dp)) + Text(text = "21km Поулмарафон($75)", fontSize = fontSizeSecond) + } + + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = checked3, + onCheckedChange = { isChecked -> + checked3 = isChecked + if (isChecked) { + finalSum += 20 + } else { + finalSum -= 20 + } + } + ) + Spacer(modifier = Modifier.width(8.dp)) + Text(text = "5km Малая дистанция($20)", fontSize = fontSizeSecond) + } + + Text( + text = "Детали спонсорства", + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier + ) + + FieldWithDropdown( + label = "Взнос:", + options = fondsNames, + selectedOption = vznos, + onOptionSelected = { vznos = it }, + fontSize = fontSizeSecond, + labelWidth = 0.5f + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabelInt( + label = "Сумма взноса:", + value = finalSum, + onValueChange = { finalSum = it }, + placeholder = "$$$", + fontSize = fontSizeSecond, + labelWidth = 0.5f, + enabled = false + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + ) { + Button( + onClick = { + val selectedFond = fonds.find { it.name == vznos } + if (selectedFond != null && selectedFond.balance > finalSum) { + val result = viewModel.withdrawalBalance(selectedFond.id, finalSum) + val result2 = viewModel.updateUserActive(userId) + if (result && result2){ + navigator.push(ConfirmRunnerScreen(timerViewModel, userId)) + } + } + }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Регистрация", fontSize = fontSizeThird) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Button( + onClick = { navigator.push(InfoRunnerScreen(timerViewModel, userId)) }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Отмена", fontSize = fontSizeThird) + } + } + } + + Column(modifier = Modifier.weight(1f)) { + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Варианты комплектов", + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + options.forEach { text -> + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + RadioButton( + selected = (text == selectedOption), + onClick = { + val newAddonPrice = when (text) { + options[0] -> 0 + options[1] -> 20 + options[2] -> 45 + else -> 0 + } + + finalSum = finalSum - previousAddonPrice + newAddonPrice + + previousAddonPrice = newAddonPrice + selectedOption = text + } + ) + Spacer(modifier = Modifier.weight(0.01f)) + Text(text = text, fontSize = fontSizeSecond) + } + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "Регистрационный взнос", + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.align(Alignment.Center) + ) + } + + Spacer(modifier = Modifier.weight(0.1f)) + + Box(modifier = Modifier.fillMaxWidth()) { + Text( + text = "$" + finalSum.toString(), + color = Color.LightGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFourth, + modifier = Modifier.align(Alignment.Center) + ) + } + } + } + } + } + } + } + } + + @Composable + private fun RegMaraphonTopBar() { + val navigator = LocalNavigator.currentOrThrow + + Box { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + ) { + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { navigator.push(InfoRunnerScreen(timerViewModel, userId)) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + + Spacer(modifier = Modifier.width(64.dp)) + + Button( + onClick = { navigator.push(MainScreen()) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Logout", + color = Color.Black, + fontSize = fontSizeButton + ) + } + } + } + } + } + + + + @Composable + private fun RegMaraphonBottomBar(timerViewModel: TimerViewModel) { + val remainingTime = timerViewModel.remainingTime.collectAsState().value + Box { + BoxWithConstraints( + modifier = Modifier + .align(Alignment.Center) + ) { + val fontSize = (maxWidth.value / 50).sp + + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = fontSize + ) + } + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/RegRunner.kt b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/RegRunner.kt new file mode 100644 index 0000000..b1e5b0e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/org/example/project/ui/Screens/RegRunner.kt @@ -0,0 +1,540 @@ +package org.example.project.ui.Screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +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.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.Scaffold +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DateRange +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +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.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import cafe.adriel.voyager.core.screen.Screen +import cafe.adriel.voyager.navigator.LocalNavigator +import cafe.adriel.voyager.navigator.currentOrThrow +import org.example.project.Models.User +import org.example.project.ViewModel.DatePickerField +import org.example.project.ViewModel.FieldWithDropdown +import org.example.project.ViewModel.FieldWithLabel +import org.example.project.ViewModel.GetPath +import org.example.project.ViewModel.ImageFromPath +import org.example.project.ViewModel.TimerViewModel +import org.example.project.ViewModel.UserViewModel +import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.time.format.DateTimeParseException + +class RegRunnerScreen( + private val timerViewModel: TimerViewModel, +) : Screen { + + @Composable + override fun Content() { + val viewModel = UserViewModel() + var users = viewModel.getUsers() + var email by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + var secondPassword by remember { mutableStateOf("") } + var name by remember { mutableStateOf("") } + var surname by remember { mutableStateOf("") } + var gender by remember { mutableStateOf("") } + var country by remember { mutableStateOf("") } + var photoPath by remember { mutableStateOf("") } + var openPicker by remember { mutableStateOf(false) } + if (openPicker) { + GetPath { selected -> + photoPath = selected + openPicker = false + } + } + var showDatePicker by remember { mutableStateOf(false) } + var dateBirthday by remember { mutableStateOf("") } + if (showDatePicker) { + DatePickerField( + onDateSelected = { + dateBirthday = it + }, + trigger = true, + onDismissRequest = { showDatePicker = false } + ) + } + + Scaffold( + topBar = { RegRunnerTopBar() }, + bottomBar = { + BottomSection( + timerViewModel, + email, password, secondPassword, name, surname, gender, photoPath, dateBirthday, country + ) + } + ) { paddingValues -> + Box( + modifier = Modifier.padding(paddingValues).fillMaxSize() + ) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + .padding(horizontal = 10.dp) + ) { + val fontSize = (maxWidth.value / 60).sp + val fontSizeFirst = (maxWidth.value / 30).sp + val fontSizeSecond = (maxWidth.value / 40).sp + val labelWidth = 0.5f + + Column( + modifier = Modifier + .fillMaxWidth().padding(bottom = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text( + text = "Регистрация бегуна", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSizeFirst, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(Modifier.weight(0.1f)) + + Text( + text = "Пожалуйста, заполните всю информацию, чтобы зарегестрироваться в качестве участника", + color = Color.DarkGray, + textAlign = TextAlign.Center, + fontSize = fontSizeSecond, + modifier = Modifier.fillMaxWidth(), + ) + + Spacer(Modifier.weight(0.2f)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(16.dp) + ) { + Column(modifier = Modifier.weight(0.8f)) { + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabel( + label = "Email:", + value = email, + onValueChange = { email = it + println(email)}, + placeholder = "Email", + fontSize = fontSize, + labelWidth = labelWidth, + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabel( + label = "Пароль:", + value = password, + onValueChange = { password = it }, + placeholder = "Пароль", + fontSize = fontSize, + labelWidth = labelWidth, + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabel( + label = "Повторите пароль:", + value = secondPassword, + onValueChange = { secondPassword = it }, + placeholder = "Повторите пароль", + fontSize = fontSize, + labelWidth = labelWidth, + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabel( + label = "Имя:", + value = name, + onValueChange = { name = it }, + placeholder = "Имя", + fontSize = fontSize, + labelWidth = labelWidth, + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithLabel( + label = "Фамилия:", + value = surname, + onValueChange = { surname = it }, + placeholder = "Фамилия", + fontSize = fontSize, + labelWidth = labelWidth, + ) + + Spacer(modifier = Modifier.weight(0.1f)) + + FieldWithDropdown( + label = "Пол:", + options = listOf("Мужской", "Остальное"), + selectedOption = gender, + onOptionSelected = { gender = it }, + fontSize = fontSize, + labelWidth = labelWidth + ) + } + + Column(modifier = Modifier.weight(1f)) { + ImageFromPath( + path = photoPath, + modifier = Modifier + .weight(0.4f) + .clip(RoundedCornerShape(8.dp)) + .border(1.dp, Color.Gray, RoundedCornerShape(8.dp)).align(Alignment.End) + ) + + Spacer(modifier = Modifier.weight(0.05f)) + + Column(modifier = Modifier.weight(0.3f)) { + Text( + text = "Фото файл:", + color = Color.DarkGray, + textAlign = TextAlign.Start, + fontSize = fontSize, + modifier = Modifier + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth() + ) { + + OutlinedTextField( + value = photoPath, + onValueChange = {photoPath = it}, + placeholder = { + Text( + text = "Photo_logo.jpg", + fontSize = fontSize, + color = Color.LightGray + ) + }, + textStyle = androidx.compose.ui.text.TextStyle( + fontSize = fontSize, + color = Color.Black + ), + enabled = true, + colors = androidx.compose.material.TextFieldDefaults.outlinedTextFieldColors( + backgroundColor = Color.White, + cursorColor = Color.Black, + textColor = Color.Black, + placeholderColor = Color.LightGray, + focusedBorderColor = Color(0xFFCCCCCC), + unfocusedBorderColor = Color(0xFFCCCCCC), + disabledBorderColor = Color(0xFFCCCCCC) + ), + modifier = Modifier.weight(0.6f).wrapContentWidth().fillMaxWidth() + ) + + Spacer(modifier = Modifier.weight(0.05f)) + + Button( + onClick = { openPicker = true}, + modifier = Modifier + .fillMaxWidth() + .weight(0.5f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text( + "Просмотр", + fontSize = fontSize + ) + } + } + + Spacer(modifier = Modifier.weight(0.05f)) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = Modifier.fillMaxWidth() + ) { + Box(modifier = Modifier.weight(labelWidth)) { + Text( + text = "Дата рождения", + color = Color.DarkGray, + textAlign = TextAlign.Start, + fontSize = fontSize, + modifier = Modifier + ) + } + + OutlinedTextField( + value = dateBirthday, + onValueChange = {dateBirthday = it}, + placeholder = { + Text( + text = "...", + fontSize = fontSize, + color = Color.LightGray + ) + }, + textStyle = androidx.compose.ui.text.TextStyle( + fontSize = fontSize, + color = Color.Black + ), + enabled = true, + colors = androidx.compose.material.TextFieldDefaults.outlinedTextFieldColors( + backgroundColor = Color.White, + cursorColor = Color.Black, + textColor = Color.Black, + placeholderColor = Color.LightGray, + focusedBorderColor = Color(0xFFCCCCCC), + unfocusedBorderColor = Color(0xFFCCCCCC), + disabledBorderColor = Color(0xFFCCCCCC) + ), + modifier = Modifier.weight(0.9f).wrapContentWidth() + ) + + Spacer(modifier = Modifier.weight(0.05f)) + + Box( + modifier = Modifier + .weight(0.5f) + .fillMaxWidth() + .clip(RoundedCornerShape(15.dp)) + .background(Color.LightGray) + .clickable { showDatePicker = true } + .padding(vertical = 8.dp), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Default.DateRange, + contentDescription = "Выбрать дату", + tint = Color.Black, + modifier = Modifier.size(24.dp) + ) + } + } + Spacer(modifier = Modifier.weight(0.05f)) + + FieldWithDropdown( + label = "Страна:", + options = listOf("Russia", "Other"), + selectedOption = country, + onOptionSelected = { country = it }, + fontSize = fontSize, + labelWidth = labelWidth + ) + + Spacer(modifier = Modifier.width(16.dp)) + } + } + } + } + } + } + } + } + + @Composable + private fun RegRunnerTopBar() { + val navigator = LocalNavigator.currentOrThrow + + Box { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .align(Alignment.Center) + ) { + val fontSizeButton = (maxWidth.value / 50).sp + val fontSizeText = (maxWidth.value / 25).sp + + Row( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + + Button( + onClick = { navigator.push(MainScreen()) }, + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + elevation = null, + shape = RoundedCornerShape(15.dp), + modifier = Modifier + .wrapContentWidth() + + ) { + Text( + text = "Назад", + color = Color.Black, + fontSize = fontSizeButton + ) + } + + Spacer(modifier = Modifier.width(24.dp)) + + Text( + text = "MARATHON SKILLS 2016", + style = MaterialTheme.typography.h6, + modifier = Modifier.weight(1f), + textAlign = TextAlign.Left, + fontWeight = FontWeight.Bold, + fontSize = fontSizeText, + color = Color.White + ) + + Spacer(modifier = Modifier.width(64.dp)) + } + } + } + } + + @Composable + private fun RegRunnerBottomBar(timerViewModel: TimerViewModel) { + val remainingTime = timerViewModel.remainingTime.collectAsState().value + + Box( + modifier = Modifier + .fillMaxWidth() + .background(Color.DarkGray) + .padding(8.dp), + contentAlignment = Alignment.Center + ) { + Text( + text = remainingTime, + color = Color.White, + fontSize = 16.sp + ) + } + } + + @Composable + private fun BottomSection( + timerViewModel: TimerViewModel, + email: String, + password: String, + secondPassword: String, + name: String, + surname: String, + gender: String, + photoPath: String, + dateBirthday: String, + country: String + ){ + val navigator = LocalNavigator.currentOrThrow + + Column( + modifier = Modifier + .fillMaxWidth() + .background(Color.White) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) + ) { + Button( + onClick = { + val viewModel = UserViewModel() + var users = viewModel.getUsers() + val nextId = (users.maxByOrNull { it.id }?.id ?: 0) + 1 + val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy") + val birthdayDate = if (dateBirthday.isNotEmpty()) { + try { + LocalDate.parse(dateBirthday, formatter) + } catch (e: DateTimeParseException) { + LocalDate.now() + } + } else { + LocalDate.now() + } + + if (secondPassword == password) { + val result = viewModel.regUser( + User( + id = nextId, + email = email, + password = password, + name = name, + surname = surname, + gender = gender, + photopath = photoPath, + birthday = birthdayDate, + country = country, + active = false + ) + ) + if (result){ + navigator.push(InfoRunnerScreen(timerViewModel, nextId)) + } + } + }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Регистрация") + } + + Spacer(modifier = Modifier.width(16.dp)) + + Button( + onClick = { navigator.push(MainScreen()) }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + backgroundColor = Color.LightGray, + contentColor = Color.Black + ), + shape = RoundedCornerShape(15.dp) + ) { + Text("Отмена") + } + } + + RegRunnerBottomBar(timerViewModel = timerViewModel) + } + } +} diff --git a/composeApp/src/desktopMain/kotlin/Tables/Connect.kt b/composeApp/src/desktopMain/kotlin/Tables/Connect.kt new file mode 100644 index 0000000..0fd39f9 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/Tables/Connect.kt @@ -0,0 +1,17 @@ +package org.example.project.Tables + +import org.example.project.Models.User +import org.jetbrains.exposed.sql.Database +import org.jetbrains.exposed.sql.SchemaUtils +import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction + +fun connectToDatabase() { + Database.connect( + "jdbc:postgresql://localhost:5447/multidb", + driver = "org.postgresql.Driver", + user = "multidb", + password = "multidb" + ) +} diff --git a/composeApp/src/desktopMain/kotlin/Tables/Fonds.kt b/composeApp/src/desktopMain/kotlin/Tables/Fonds.kt new file mode 100644 index 0000000..2b2b8b4 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/Tables/Fonds.kt @@ -0,0 +1,13 @@ +package org.example.project.Tables + +import org.example.project.Tables.Users.autoIncrement +import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.sql.javatime.date + +object Fonds : Table() { + val id = integer("id").autoIncrement() + val name = varchar("name", 255) + val balance = integer("balance") + + override val primaryKey = PrimaryKey(id) +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/Tables/Users.kt b/composeApp/src/desktopMain/kotlin/Tables/Users.kt new file mode 100644 index 0000000..d2a3490 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/Tables/Users.kt @@ -0,0 +1,18 @@ +package org.example.project.Tables + +import org.jetbrains.exposed.sql.Table +import org.jetbrains.exposed.sql.javatime.date + +object Users : Table() { + val id = integer("id").autoIncrement() + val email = varchar("email", 255) + val password = varchar("password", 255) + val name = varchar("name", 255) + val surname = varchar("surname", 255) + val gender = varchar("gender", 255) + val photopath = varchar("photopath", 255) + val birthday = date("birthday") + val country = varchar("country", 255) + val active = bool("active").default(false) + override val primaryKey = PrimaryKey(id) +} diff --git a/composeApp/src/desktopMain/kotlin/ViewModel/DatePickerField.kt b/composeApp/src/desktopMain/kotlin/ViewModel/DatePickerField.kt new file mode 100644 index 0000000..36155f4 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/ViewModel/DatePickerField.kt @@ -0,0 +1,38 @@ +package org.example.project.ViewModel + +import androidx.compose.runtime.* +import java.text.SimpleDateFormat +import javax.swing.* +import java.util.* + +@Composable +actual fun DatePickerField( + onDateSelected: (String) -> Unit, + trigger: Boolean, + onDismissRequest: () -> Unit +) { + if (trigger) { + LaunchedEffect(Unit) { + val dialog = JDialog() + dialog.title = "Выбор даты" + val model = SpinnerDateModel() + val spinner = JSpinner(model) + val panel = JPanel() + val button = JButton("OK") + panel.add(spinner) + panel.add(button) + dialog.contentPane = panel + dialog.setSize(200, 100) + + button.addActionListener { + val date = model.date + val formatted = SimpleDateFormat("dd.MM.yyyy").format(date) + onDateSelected(formatted) + dialog.dispose() + onDismissRequest() + } + + dialog.isVisible = true + } + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/ViewModel/GetPath.kt b/composeApp/src/desktopMain/kotlin/ViewModel/GetPath.kt new file mode 100644 index 0000000..9880649 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/ViewModel/GetPath.kt @@ -0,0 +1,29 @@ +package org.example.project.ViewModel + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import javax.swing.JFileChooser + +@Composable +actual fun GetPath(onPathSelected: (String) -> Unit) { + LaunchedEffect(Unit) { + val selectedPath = withContext(Dispatchers.IO) { + val chooser = JFileChooser() + chooser.dialogTitle = "Выберите изображение" + chooser.fileSelectionMode = JFileChooser.FILES_ONLY + + val result = chooser.showOpenDialog(null) + if (result == JFileChooser.APPROVE_OPTION) { + val file: File = chooser.selectedFile + file.absolutePath + } else { + "" + } + } + + onPathSelected(selectedPath) + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/ViewModel/IconPrinter.kt b/composeApp/src/desktopMain/kotlin/ViewModel/IconPrinter.kt new file mode 100644 index 0000000..3744214 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/ViewModel/IconPrinter.kt @@ -0,0 +1,33 @@ +package org.example.project.ViewModel + +import androidx.compose.runtime.Composable +import androidx.compose.foundation.Image +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.graphics.toComposeImageBitmap +import androidx.compose.ui.layout.ContentScale +import org.jetbrains.skia.Image +import java.io.IOException + +@Composable +actual fun IconPrinter(resourceName: String, modifier: Modifier) { + val imageBitmap = try { + val path = "drawable/${resourceName}.jpg" + val stream = Thread.currentThread().contextClassLoader.getResourceAsStream(path) + if (stream != null) { + Image.makeFromEncoded(stream.readAllBytes()).toComposeImageBitmap() + } else null + } catch (e: IOException) { + println("Ошибка загрузки иконки: ${e.message}") + null + } + + if (imageBitmap != null) { + Image( + painter = BitmapPainter(imageBitmap), + contentDescription = null, + modifier = modifier, + contentScale = ContentScale.FillBounds + ) + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/ViewModel/ImageFromPath.kt b/composeApp/src/desktopMain/kotlin/ViewModel/ImageFromPath.kt new file mode 100644 index 0000000..479d9f6 --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/ViewModel/ImageFromPath.kt @@ -0,0 +1,45 @@ +package org.example.project.ViewModel + +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.ImageBitmap +import androidx.compose.ui.graphics.painter.BitmapPainter +import androidx.compose.ui.graphics.toComposeImageBitmap +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.ContentScale +import org.jetbrains.skia.Image +import java.io.File +import java.nio.file.Files + +@Composable +actual fun ImageFromPath(path: String, modifier: Modifier) { + val imageBitmap: ImageBitmap? = try { + if (path.isBlank()) { + val resourceStream = Thread.currentThread().contextClassLoader.getResourceAsStream("drawable/defPhoto.png") + val bytes = resourceStream.readAllBytes() + Image.makeFromEncoded(bytes).toComposeImageBitmap() + } else { + val file = File(path) + if (file.exists()) { + val bytes = Files.readAllBytes(file.toPath()) + Image.makeFromEncoded(bytes).toComposeImageBitmap() + } else null + } + } catch (e: Exception) { + println("Ошибка загрузки изображения: ${e.message}") + null + } + + if (imageBitmap != null) { + Image( + painter = BitmapPainter(imageBitmap), + contentDescription = null, + modifier = modifier, + contentScale = ContentScale.Crop + ) + } else { + Box(modifier = modifier.fillMaxSize()) + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/ViewModel/UserRepository.kt b/composeApp/src/desktopMain/kotlin/ViewModel/UserRepository.kt new file mode 100644 index 0000000..04bc18f --- /dev/null +++ b/composeApp/src/desktopMain/kotlin/ViewModel/UserRepository.kt @@ -0,0 +1,128 @@ +package org.example.project.ViewModel + +import org.example.project.Models.Fond +import org.example.project.Models.User +import org.example.project.Tables.Fonds +import org.example.project.Tables.Users +import org.example.project.Tables.connectToDatabase +import org.jetbrains.exposed.sql.SchemaUtils +import org.jetbrains.exposed.sql.SqlExpressionBuilder +import org.jetbrains.exposed.sql.insert +import org.jetbrains.exposed.sql.selectAll +import org.jetbrains.exposed.sql.transactions.transaction +import org.jetbrains.exposed.sql.update + +actual class UserRepository { + actual fun fetchUsers(): List { + connectToDatabase() + + return transaction { + Users.selectAll().map { + User( + id = it[Users.id], + email = it[Users.email], + password = it[Users.password], + name = it[Users.name], + surname = it[Users.surname], + gender = it[Users.gender], + photopath = it[Users.photopath], + birthday = it[Users.birthday], + country = it[Users.country], + active = it[Users.active] + ) + } + } + } + actual fun regUser(user: User): Boolean{ + connectToDatabase() + + return try { + transaction { + Users.insert { + it[id] = user.id + it[email] = user.email + it[password] = user.password + it[name] = user.name + it[surname] = user.surname + it[gender] = user.gender + it[photopath] = user.photopath + it[birthday] = user.birthday + it[country] = user.country + } + } + true + } catch (e: Exception) { + println("Ошибка при регистрации: ${e.message}") + false + } + } + actual fun fetchFonds(): List{ + connectToDatabase() + + return transaction { + Fonds.selectAll().map { + Fond( + id = it[Fonds.id], + name = it[Fonds.name], + balance = it[Fonds.balance] + ) + } + } + } + actual fun withdrawalBalance(idFond: Int, dedSum: Int): Boolean{ + connectToDatabase() + + return try { + transaction { + Fonds.update({Fonds.id eq idFond}){ + with(SqlExpressionBuilder) { + it.update(balance, balance - dedSum) + } + } + } + true + } catch (e: Exception){ + println("Ошибка при снятии: ${e.message}") + false + } + } + actual fun getUserIdByEmail(email: String): Int { + connectToDatabase() + + return transaction { + Users.selectAll().where { Users.email eq email }.single()[Users.id] + } + } + actual fun updateUserActive(idUser: Int): Boolean{ + connectToDatabase() + + return try { + transaction { + Users.update({Users.id eq idUser}){ + it[active] = true + } + } + true + } catch (e: Exception){ + println("Ошибка при обновлении: ${e.message}") + false + } + } + actual fun topUpBalance(idFond: Int, sum: Int): Boolean{ + connectToDatabase() + + return try { + transaction { + Fonds.update({Fonds.id eq idFond}){ + with(SqlExpressionBuilder) { + it.update(balance, balance + sum) + } + } + } + true + } catch (e: Exception){ + println("Ошибка при пополнении: ${e.message}") + false + } + } +} \ No newline at end of file diff --git a/composeApp/src/desktopMain/kotlin/org/example/project/main.kt b/composeApp/src/desktopMain/kotlin/org/example/project/main.kt index af78418..2e5f871 100644 --- a/composeApp/src/desktopMain/kotlin/org/example/project/main.kt +++ b/composeApp/src/desktopMain/kotlin/org/example/project/main.kt @@ -5,6 +5,7 @@ import androidx.compose.ui.window.Window import org.example.project.App fun main() = application { + Window( onCloseRequest = ::exitApplication, title = "Marathon Skills 2016"