Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.example.project

import android.app.Application
import org.example.project.di.initKoin
import initKoin

class MyApp : Application() {
override fun onCreate() {
Expand Down
16 changes: 16 additions & 0 deletions composeApp/src/commonMain/composeResources/values-ar/string.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,20 @@
<string name="next">التالى</string>
<string name="skip">تخطى</string>


<!-- LocationSetupScreen Strings -->
<string name="location_title">أين موقعك؟</string>
<string name="location_description">الموقع يساعد في تحسين الدقة، لكن لا تقلق، يمكنك تحديثه لاحقًا.</string>
<string name="location_hint">المحافظة، المنطقة</string>
<string name="choose_governorate">اختر المحافظة</string>
<string name="choose_district">اختر المنطقة</string>
<string name="next_button">التالي</string>
<string name="location_icon">أيقونة الموقع</string>
<string name="dropdown_icon">أيقونة القائمة المنسدلة</string>
<string name="enter_detailed_location">أدخل موقعك بالتفصيل</string>
<string name="back_arrow">العوده</string>
<string name="down_arrow">السهم لأسفل</string>



</resources>
17 changes: 17 additions & 0 deletions composeApp/src/commonMain/composeResources/values/string.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,21 @@
<string name="account_setup_craftsman_category_title">What services do you offer?</string>
<string name="account_setup_craftsman_category_description">Choose your specialties to get relevant job requests. You can change this later.</string>


<!-- LocationSetupScreen Strings -->
<string name="location_title">Where are you located?</string>
<string name="location_description">Location helps improve accuracy, but don\'t worry, you can update it later.</string>
<string name="location_hint">Governorate, District</string>
<string name="choose_governorate">Choose Governorate</string>
<string name="choose_district">Choose District</string>
<string name="next_button">Next</string>
<string name="location_icon">Location icon</string>
<string name="dropdown_icon">Dropdown</string>
<string name="enter_detailed_location">Enter your location in detail</string>
<string name="back_arrow">back arrow</string>
<string name="down_arrow">back arrow</string>




</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.example.project.data.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class DistrictDto(
@SerialName("id") val id: String,
@SerialName("name") val name: String,
@SerialName("governorateId") val governorateId: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.example.project.data.dto

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class GovernoratesDto(
@SerialName("id") val id: String,
@SerialName("name") val name: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.example.project.data.repository

import io.ktor.client.HttpClient
import io.ktor.client.request.get
import io.ktor.client.call.body
import org.example.project.data.dto.DistrictDto
import org.example.project.data.dto.GovernoratesDto
import org.example.project.data.repository.mapper.toDistrict
import org.example.project.data.repository.mapper.toGovernorates
import org.example.project.data.utils.NetworkConstants.DISTRICT_ENDPOINT
import org.example.project.data.utils.NetworkConstants.GOVERNORATES_ENDPOINT
import org.example.project.data.utils.NetworkConstants.LOCATION_PATH
import org.example.project.domain.entity.District
import org.example.project.domain.entity.Governorates
import org.example.project.domain.repository.LocationRepository

class LocationRepositoryImpl(
private val httpClient: HttpClient
) : LocationRepository {

override suspend fun getAllGovernorates(): List<Governorates> {
val response = httpClient.get("/$LOCATION_PATH/$GOVERNORATES_ENDPOINT")
val body: List<GovernoratesDto> = response.body()
return body.toGovernorates()
}

override suspend fun getDistrictsByGovernorateId(governorateId: String): List<District> {
val response = httpClient.get("/$LOCATION_PATH/$DISTRICT_ENDPOINT/$governorateId")
val body: List<DistrictDto> = response.body()
return body.toDistrict()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.example.project.data.repository.mapper

import org.example.project.data.dto.DistrictDto
import org.example.project.domain.entity.District

fun List<DistrictDto>.toDistrict(): List<District> {
return map { dto ->
District(
id = dto.id,
name = dto.name,
governorateId = dto.governorateId
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.example.project.data.repository.mapper

import org.example.project.data.dto.GovernoratesDto
import org.example.project.domain.entity.Governorates

fun List<GovernoratesDto>.toGovernorates(): List<Governorates> {
return map { dto ->
Governorates(
id = dto.id,
name = dto.name
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@ package org.example.project.data.utils

object NetworkConstants {
const val ONBOARDING_END_POINT = "/onboarding"

const val LOCATION_PATH = "location"
const val GOVERNORATES_ENDPOINT = "governorates"
const val DISTRICT_ENDPOINT = "district"
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.example.project.di

import org.koin.core.annotation.ComponentScan
import org.example.project.data.repository.LocationRepositoryImpl
import org.example.project.domain.repository.LocationRepository
import org.koin.core.annotation.Module
import org.koin.dsl.module

@Module
@ComponentScan("org.example.project")
class CraftoModule
class CraftoModule {
val module = module {
single<LocationRepository> { LocationRepositoryImpl(get()) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.example.project.di

import org.example.project.presentation.screens.setupScreens.location.LocationViewModel
import org.koin.core.module.dsl.viewModel
import org.koin.dsl.module

class ViewModelModule {
val module = module {
viewModel { LocationViewModel(get()) }
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package org.example.project.di

import org.example.project.di.NetworkModule
import org.example.project.di.ViewModelModule
import org.koin.core.context.startKoin
import org.koin.dsl.KoinAppDeclaration
import org.koin.ksp.generated.module

fun initKoin(config: KoinAppDeclaration? = null) {
startKoin {
config?.invoke(this)
modules(CraftoModule().module, NetworkModule().module)
modules(
CraftoModule().module,
ViewModelModule().module,
NetworkModule().module
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.example.project.domain.entity

data class District(
val id: String,
val name: String,
val governorateId: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.example.project.domain.entity

data class Governorates(
val id: String,
val name: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.example.project.domain.repository

import org.example.project.domain.entity.District
import org.example.project.domain.entity.Governorates

interface LocationRepository {
suspend fun getAllGovernorates(): List<Governorates>
suspend fun getDistrictsByGovernorateId(governorateId: String): List<District>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.example.project.presentation.components

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import org.example.project.presentation.designsystem.components.TextField
import org.jetbrains.compose.resources.stringResource
import crafto.composeapp.generated.resources.Res
import crafto.composeapp.generated.resources.enter_detailed_location

@Composable
fun DetailLocationInput(
text: String,
onTextChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
TextField(
hint = stringResource(Res.string.enter_detailed_location),
text = text,
onTextChange = onTextChange,
modifier = modifier.fillMaxWidth()
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.example.project.presentation.components

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import org.example.project.presentation.designsystem.components.BottomSheet
import org.example.project.presentation.designsystem.textstyle.AppTheme
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import crafto.composeapp.generated.resources.Res
import crafto.composeapp.generated.resources.alt_arrow_down
import crafto.composeapp.generated.resources.down_arrow

@Composable
fun <T> DropdownBottomSheet(
show: Boolean,
items: List<T>,
itemLabel: (T) -> String,
onDismiss: () -> Unit,
onSelect: (T) -> Unit
) {
if (show && items.isNotEmpty()) {
BottomSheet(onDismissRequest = onDismiss) {
LazyColumn {
items(items) { item ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onSelect(item) }
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = itemLabel(item),
style = AppTheme.textStyle.body.medium,
color = AppTheme.craftoColors.shade.primary,
modifier = Modifier.weight(1f)
)
Icon(
painter = painterResource(Res.drawable.alt_arrow_down),
contentDescription = stringResource(Res.string.down_arrow),
tint = AppTheme.craftoColors.shade.secondary
)
}
}
}
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the file name is Location, but it has GovernorateSelector and DetailLocationInput, I don't get the idea of the file, if you use them as reusable components for the location screen, you can have them in 2 different files, of have a meaningful file name

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.example.project.presentation.components

import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import org.example.project.presentation.designsystem.components.TextField
import org.example.project.presentation.designsystem.textstyle.AppTheme
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import crafto.composeapp.generated.resources.*

@Composable
fun DropdownSelector(
text: String,
hint: String,
icon: DrawableResource,
hasSelection: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier
.fillMaxWidth()
.height(48.dp)
.background(
color = AppTheme.craftoColors.background.card,
shape = RoundedCornerShape(AppTheme.craftoRadius.lg)
)
.clickable(
onClick = onClick,
indication = null,
interactionSource = remember { MutableInteractionSource() }
)
.semantics { role = Role.Button },
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TextField(
hint = hint,
text = text,
onTextChange = {},
readOnlyMode = true,
enabledState = false,
startIcon = {
Icon(
painter = painterResource(icon),
contentDescription = null,
tint = if (hasSelection) AppTheme.craftoColors.shade.primary
else AppTheme.craftoColors.shade.tertiary
)
},
endIcon = {
Icon(
painter = painterResource(Res.drawable.alt_arrow_down),
contentDescription = stringResource(Res.string.dropdown_icon),
tint = if (hasSelection) AppTheme.craftoColors.shade.primary
else AppTheme.craftoColors.shade.tertiary
)
}
)
}
}
Loading