diff --git a/composeApp/src/commonMain/composeResources/drawable/otp.png b/composeApp/src/commonMain/composeResources/drawable/otp.png
new file mode 100644
index 0000000..0bdcf21
Binary files /dev/null and b/composeApp/src/commonMain/composeResources/drawable/otp.png differ
diff --git a/composeApp/src/commonMain/composeResources/values-ar/string.xml b/composeApp/src/commonMain/composeResources/values-ar/string.xml
index 4659eed..3319494 100644
--- a/composeApp/src/commonMain/composeResources/values-ar/string.xml
+++ b/composeApp/src/commonMain/composeResources/values-ar/string.xml
@@ -8,7 +8,7 @@
مرحبًا بك في صنعة 👋
أدخل رقم هاتفك للمتابعة
- +٢٠ ٠٠٠ - ٠٠٠ - ٠٠٠٠
+ ٠٠٠ - ٠٠٠ - ٠٠٠٠
الشروط والأحكام
سياسة الخصوصية
بالمتابعة فإنك توافق على
@@ -24,4 +24,23 @@
التالى
تخطى
+
+ أدخل رمز التحقق
+ لقد أرسلنا رمزًا مكوّنًا من 6 أرقام إلى رقم هاتفك
+ تحقق
+ لم تستلم الرمز؟
+ إعادة إرسال الرمز بعد
+ إعادة إرسال الرمز
+
+
+
+
+
+ أدخل رمز التحقق
+ لقد أرسلنا رمزًا مكوّنًا من 4 أرقام إلى رقم هاتفك
+ تحقق
+ لم تستلم الرمز؟
+ إعادة إرسال الرمز بعد
+ إعادة إرسال الرمز
+
\ No newline at end of file
diff --git a/composeApp/src/commonMain/composeResources/values/string.xml b/composeApp/src/commonMain/composeResources/values/string.xml
index afd1afe..ccee94f 100644
--- a/composeApp/src/commonMain/composeResources/values/string.xml
+++ b/composeApp/src/commonMain/composeResources/values/string.xml
@@ -9,7 +9,7 @@
Welcome to San3a 👋
Enter your phone number to\n continue
- +20 000 - 000 - 0000
+ 000 - 000 - 0000
Terms and Conditions
Privacy Policy
By continuing, you agree on our
@@ -20,12 +20,27 @@
logo icon
egypt flag
+
+ Enter Verification Code
+ We sent 6-digit code to your phone number
+ Verify
+ Didn't receive code?
+ Resend Code after
+ Resend Code
+
Get Started
Next
Skip
+
+ Enter Verification Code
+ We sent 4-digit code to your phone number
+ Verify
+ Didn't receive code?
+ Resend Code after
+ Resend Code
What do you usually need help with?
@@ -33,4 +48,5 @@
What services do you offer?
Choose your specialties to get relevant job requests. You can change this later.
+
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/org/example/project/presentation/designsystem/components/BackButton.kt b/composeApp/src/commonMain/kotlin/org/example/project/presentation/designsystem/components/BackButton.kt
new file mode 100644
index 0000000..041d77d
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/org/example/project/presentation/designsystem/components/BackButton.kt
@@ -0,0 +1,48 @@
+package org.example.project.presentation.designsystem.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import crafto.composeapp.generated.resources.Res
+import crafto.composeapp.generated.resources.arrow_left
+import org.example.project.presentation.designsystem.textstyle.AppTheme
+import org.jetbrains.compose.resources.painterResource
+import org.jetbrains.compose.ui.tooling.preview.Preview
+
+@Composable
+fun BackButton(
+ onClick : () -> Unit,
+ modifier: Modifier = Modifier,
+){
+ Box(
+ modifier = modifier
+ ){
+ Icon(
+ painter = painterResource(Res.drawable.arrow_left),
+ contentDescription = "arrow left icon",
+ modifier = Modifier.align(Alignment.Center).clickable(onClick = onClick)
+ .padding(12.dp)
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun BackButtonPreview(){
+ AppTheme(isDarkTheme = false) {
+ BackButton(
+ modifier = Modifier.background(
+ color = AppTheme.craftoColors.background.card,
+ shape = RoundedCornerShape(AppTheme.craftoRadius.full)
+ ),
+ onClick = {}
+ )
+ }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/org/example/project/presentation/designsystem/components/TextField.kt b/composeApp/src/commonMain/kotlin/org/example/project/presentation/designsystem/components/TextField.kt
index fdb59f1..e294fa1 100644
--- a/composeApp/src/commonMain/kotlin/org/example/project/presentation/designsystem/components/TextField.kt
+++ b/composeApp/src/commonMain/kotlin/org/example/project/presentation/designsystem/components/TextField.kt
@@ -3,6 +3,7 @@ package org.example.project.presentation.designsystem.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -14,8 +15,16 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
+import androidx.compose.material3.Text
+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
@@ -35,12 +44,10 @@ import org.example.project.presentation.designsystem.textstyle.AppTheme
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.ui.tooling.preview.Preview
-private const val s = "Forgot Password?"
-
@Composable
fun TextField(
labelText: String? = null,
- showDividerLine : Boolean = false,
+ showDividerLine: Boolean = false,
hint: String? = null,
text: String,
onTextChange: (String) -> Unit,
@@ -59,6 +66,7 @@ fun TextField(
allowSingleLine: Boolean = true,
textAppearance: TextStyle? = null,
textTint: Color? = null,
+ showPhoneCode: Boolean = false,
inputKeyboard: KeyboardOptions = KeyboardOptions.Default.copy(imeAction = androidx.compose.ui.text.input.ImeAction.Done),
inputActions: KeyboardActions = KeyboardActions.Default,
forgotAction: (() -> Unit)? = null,
@@ -98,7 +106,7 @@ fun TextField(
.background(
AppTheme.craftoColors.background.card,
RoundedCornerShape(AppTheme.craftoRadius.lg)
- ),
+ ).focusable(),
textStyle = textAppearance ?: AppTheme.textStyle.body.medium,
placeholder = hint?.let {
{
@@ -117,9 +125,20 @@ fun TextField(
isError = errorState,
enabled = enabledState,
leadingIcon = {
- Row(verticalAlignment = Alignment.CenterVertically) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(4.dp)
+ ) {
startIcon?.invoke()
- if(showDividerLine) VerticalDivider()
+ if (showDividerLine) VerticalDivider()
+
+ if (showPhoneCode) {
+ Text(
+ text = "+20",
+ style = AppTheme.textStyle.body.medium,
+ color = AppTheme.craftoColors.shade.primary
+ )
+ }
}
},
trailingIcon = {
diff --git a/composeApp/src/commonMain/kotlin/org/example/project/presentation/screens/register/OTPScreen.kt b/composeApp/src/commonMain/kotlin/org/example/project/presentation/screens/register/OTPScreen.kt
new file mode 100644
index 0000000..e836720
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/org/example/project/presentation/screens/register/OTPScreen.kt
@@ -0,0 +1,264 @@
+package org.example.project.presentation.screens.register
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.ime
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.statusBars
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
+import androidx.compose.material3.Text
+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.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import crafto.composeapp.generated.resources.Res
+import crafto.composeapp.generated.resources.do_not_receive_code
+import crafto.composeapp.generated.resources.enter_verification_code
+import crafto.composeapp.generated.resources.otp
+import crafto.composeapp.generated.resources.resent_code
+import crafto.composeapp.generated.resources.sent_message_to_phone
+import crafto.composeapp.generated.resources.verify
+import org.example.project.presentation.designsystem.components.BackButton
+import org.example.project.presentation.designsystem.components.ButtonState
+import org.example.project.presentation.designsystem.components.PrimaryButton
+import org.example.project.presentation.designsystem.components.SecondaryButton
+import org.example.project.presentation.designsystem.textstyle.AppTheme
+import org.jetbrains.compose.resources.painterResource
+import org.jetbrains.compose.resources.stringResource
+import org.jetbrains.compose.ui.tooling.preview.Preview
+
+@Composable
+fun OTPScreen() {
+ OTPContent(
+ phoneNumber = "01279336697",
+ onVerifyButtonClick = {},
+ onResendCodeButtonClick = {},
+ onBackButtonClick = {}
+ )
+}
+
+@Composable
+private fun OTPContent(
+ modifier: Modifier = Modifier,
+ phoneNumber: String,
+ onVerifyButtonClick: () -> Unit,
+ onResendCodeButtonClick: () -> Unit,
+ onBackButtonClick: () -> Unit
+) {
+
+ var otpList by remember { mutableStateOf(List(6) { "" }) }
+ val focusManager = LocalFocusManager.current
+ val focusRequesters = List(otpList.size) { FocusRequester() }
+
+ Box(
+ modifier = modifier.fillMaxSize().background(AppTheme.craftoColors.background.screen)
+ ) {
+ Column(
+ modifier = Modifier.fillMaxSize(),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Box(
+ modifier = Modifier
+ .padding(start = 16.dp, top = 16.dp)
+ .windowInsetsPadding(WindowInsets.statusBars)
+ .align(Alignment.Start)
+ )
+ {
+ BackButton(
+ modifier = Modifier.background(
+ color = AppTheme.craftoColors.background.card,
+ shape = RoundedCornerShape(AppTheme.craftoRadius.full)
+ ),
+ onClick = onBackButtonClick
+ )
+ }
+
+ Image(
+ painter = painterResource(Res.drawable.otp),
+ contentDescription = "otp image",
+ )
+
+ Box(
+ modifier = Modifier.fillMaxSize().background(
+ color = AppTheme.craftoColors.background.card,
+ shape = RoundedCornerShape(
+ topStart = AppTheme.craftoRadius.x5l,
+ topEnd = AppTheme.craftoRadius.x5l
+ ),
+ ).padding(horizontal = 24.dp)
+ ) {
+ Column(
+ modifier = Modifier.verticalScroll(rememberScrollState())
+ .windowInsetsPadding(WindowInsets.ime)
+ ) {
+ Text(
+ text = stringResource(Res.string.enter_verification_code),
+ style = AppTheme.textStyle.title.medium,
+ color = AppTheme.craftoColors.shade.primary,
+ modifier = Modifier.padding(top = 40.dp, bottom = 8.dp)
+ )
+
+ Text(
+ text = stringResource(Res.string.sent_message_to_phone),
+ style = AppTheme.textStyle.body.mediumRegular,
+ color = AppTheme.craftoColors.shade.secondary,
+ )
+ Text(
+ text = phoneNumber,
+ style = AppTheme.textStyle.body.medium,
+ color = AppTheme.craftoColors.shade.primary,
+ modifier = Modifier.padding(bottom = 24.dp)
+ )
+
+ Row(
+ modifier = Modifier.padding(bottom = 24.dp)
+ ) {
+
+ repeat(otpList.size) { index ->
+ OTPField(
+ text = otpList[index],
+ modifier = Modifier.weight(1f)
+ .focusRequester(focusRequesters[index]),
+ onTextChange = { value ->
+ val digit = value.filter { it.isDigit() }.take(1)
+ val updateList = otpList.toMutableList().also { list ->
+ list[index] = digit
+ }
+ otpList = updateList
+
+ if (digit.isNotEmpty()) {
+ if (index < otpList.size - 1) {
+ focusRequesters[index + 1].requestFocus()
+ } else {
+ focusManager.clearFocus()
+ }
+ } else if (otpList[index].isEmpty() && value.isEmpty()) {
+ if (index > 0) {
+ focusRequesters[index - 1].requestFocus()
+ }
+ }
+ }
+ )
+ }
+ }
+
+ PrimaryButton(
+ text = stringResource(Res.string.verify),
+ enabled = true,
+ onClick = onVerifyButtonClick,
+ buttonState = ButtonState.Enable,
+ modifier = Modifier.fillMaxWidth(),
+ contentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp)
+ )
+
+ Text(
+ text = stringResource(Res.string.do_not_receive_code),
+ style = AppTheme.textStyle.body.mediumRegular,
+ color = AppTheme.craftoColors.shade.secondary,
+ modifier = Modifier
+ .padding(top = 24.dp, bottom = 12.dp)
+ .align(Alignment.CenterHorizontally)
+ )
+
+ SecondaryButton(
+ text = stringResource(Res.string.resent_code),
+ enabled = true,
+ onClick = onResendCodeButtonClick,
+ buttonState = ButtonState.Enable,
+ modifier = Modifier.fillMaxWidth(),
+ containerColor = AppTheme.craftoColors.shade.quinary,
+ contentPadding = PaddingValues(horizontal = 24.dp, vertical = 14.dp)
+ )
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun OTPField(
+ text: String,
+ hint: String = "0",
+ modifier: Modifier,
+ onTextChange: (String) -> Unit
+) {
+ var isFocused by remember { mutableStateOf(false) }
+
+ OutlinedTextField(
+ value = text,
+ onValueChange = onTextChange,
+ modifier = modifier.background(AppTheme.craftoColors.background.card)
+ .padding(horizontal = 5.dp, vertical = 8.dp).onFocusChanged { focusState ->
+ isFocused = focusState.isFocused
+ },
+ singleLine = true,
+ textStyle = AppTheme.textStyle.title.large.copy(
+ textAlign = TextAlign.Center,
+ color = AppTheme.craftoColors.shade.primary
+ ),
+ shape = RoundedCornerShape(AppTheme.craftoRadius.lg),
+ placeholder = {
+ Box(
+ modifier = Modifier.fillMaxWidth(),
+ contentAlignment = Alignment.Center
+ ) {
+ if (text.isEmpty() && !isFocused) {
+ Text(
+ hint,
+ textAlign = TextAlign.Center,
+ style = AppTheme.textStyle.title.large,
+ color = AppTheme.craftoColors.shade.tertiary
+ )
+ }
+ }
+ },
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedTextColor = AppTheme.craftoColors.shade.primary,
+ focusedBorderColor = AppTheme.craftoColors.brand.primary,
+ unfocusedBorderColor = AppTheme.craftoColors.stroke.primary,
+ errorBorderColor = AppTheme.craftoColors.additional.primaryRed,
+ errorTextColor = AppTheme.craftoColors.shade.primary,
+ cursorColor = AppTheme.craftoColors.brand.primary,
+ errorCursorColor = AppTheme.craftoColors.additional.primaryRed,
+ disabledTextColor = AppTheme.craftoColors.shade.primary,
+ disabledBorderColor = AppTheme.craftoColors.stroke.primary,
+ disabledPlaceholderColor = AppTheme.craftoColors.shade.tertiary,
+ disabledLabelColor = AppTheme.craftoColors.shade.tertiary,
+ ),
+ keyboardOptions = KeyboardOptions.Default.copy(
+ keyboardType = KeyboardType.Number
+ )
+ )
+}
+
+
+@Preview
+@Composable
+private fun OTPScreenPreview() {
+ AppTheme {
+ OTPScreen()
+ }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/org/example/project/presentation/screens/register/RegisterScreen.kt b/composeApp/src/commonMain/kotlin/org/example/project/presentation/screens/register/RegisterScreen.kt
index d319231..fdaf06f 100644
--- a/composeApp/src/commonMain/kotlin/org/example/project/presentation/screens/register/RegisterScreen.kt
+++ b/composeApp/src/commonMain/kotlin/org/example/project/presentation/screens/register/RegisterScreen.kt
@@ -14,11 +14,18 @@ import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
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.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import crafto.composeapp.generated.resources.Res
@@ -26,11 +33,11 @@ import crafto.composeapp.generated.resources.and_text
import crafto.composeapp.generated.resources.continue_button
import crafto.composeapp.generated.resources.egypt_flag
import crafto.composeapp.generated.resources.enter_phone
+import crafto.composeapp.generated.resources.logo
import crafto.composeapp.generated.resources.logo_icon
import crafto.composeapp.generated.resources.phone_hint
import crafto.composeapp.generated.resources.privacy_agreement
import crafto.composeapp.generated.resources.privacy_policy
-import crafto.composeapp.generated.resources.register_logo
import crafto.composeapp.generated.resources.terms_and_conditions
import crafto.composeapp.generated.resources.welcome_title
import org.example.project.presentation.designsystem.components.ButtonState
@@ -60,6 +67,7 @@ private fun RegisterContent(
onTermsClick: () -> Unit,
onButtonClick: () -> Unit
) {
+ var number by remember { mutableStateOf("") }
Box(
modifier = modifier.fillMaxSize().background(AppTheme.craftoColors.brand.primary)
) {
@@ -76,7 +84,7 @@ private fun RegisterContent(
) {
Icon(
- painter = painterResource(Res.drawable.register_logo),
+ painter = painterResource(Res.drawable.logo),
contentDescription = stringResource(Res.string.logo_icon),
modifier = Modifier.align(Alignment.Center).offset(x = (-5).dp, y = (5).dp),
tint = AppTheme.craftoColors.brand.primary
@@ -112,14 +120,20 @@ private fun RegisterContent(
startIcon = {
Image(
painter = painterResource(Res.drawable.egypt_flag),
- contentDescription = stringResource(Res.string.egypt_flag)
+ contentDescription = stringResource(Res.string.egypt_flag),
+ modifier = Modifier.padding(start = 18.dp)
)
},
showDividerLine = true,
maxLines = 1,
minLines = 1,
- text = "",
- onTextChange = {}
+ showPhoneCode = true,
+ text = number,
+ onTextChange = { if (it.length <= 10) number = it },
+ inputKeyboard = KeyboardOptions(
+ keyboardType = KeyboardType.Number,
+ imeAction = ImeAction.Default
+ )
)
PrivacyAndTextSection(