feat: add database lab_test
This commit is contained in:
@@ -2,6 +2,7 @@ plugins {
|
|||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.compose)
|
alias(libs.plugins.kotlin.compose)
|
||||||
|
alias(libs.plugins.ksp)
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -82,4 +83,9 @@ dependencies {
|
|||||||
|
|
||||||
//navigation
|
//navigation
|
||||||
implementation(libs.androidx.navigation.compose)
|
implementation(libs.androidx.navigation.compose)
|
||||||
|
|
||||||
|
// Room Database
|
||||||
|
implementation(libs.androidx.room.runtime)
|
||||||
|
implementation(libs.androidx.room.ktx)
|
||||||
|
ksp(libs.androidx.room.compiler)
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<application
|
<application
|
||||||
android:name=".DrugApp"
|
android:name=".DrugApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
|||||||
Binary file not shown.
@@ -1,7 +1,10 @@
|
|||||||
package com.approagency.drug
|
package com.approagency.drug
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import com.approagency.drug.data.local.database.LabDatabase
|
||||||
import com.approagency.drug.di.appModule
|
import com.approagency.drug.di.appModule
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
|
|
||||||
@@ -13,5 +16,10 @@ class DrugApp : Application(){
|
|||||||
modules(appModule)
|
modules(appModule)
|
||||||
printLogger()
|
printLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Preload database (optional, for faster access)
|
||||||
|
GlobalScope.launch {
|
||||||
|
LabDatabase.getInstance(this@DrugApp)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.approagency.drug.data.local.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.approagency.drug.data.local.entities.TestGroupEntity
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface TestGroupDao {
|
||||||
|
@Query("SELECT * FROM testGroup ORDER BY Id")
|
||||||
|
fun getAllGroups(): Flow<List<TestGroupEntity>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM testGroup WHERE Id = :groupId")
|
||||||
|
suspend fun getGroupById(groupId: Int): TestGroupEntity?
|
||||||
|
|
||||||
|
@Query("SELECT * FROM testGroup WHERE Isparent = '1'")
|
||||||
|
fun getParentGroups(): Flow<List<TestGroupEntity>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM testGroup WHERE Isparent = '0'")
|
||||||
|
fun getChildGroups(): Flow<List<TestGroupEntity>>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insertGroup(group: TestGroupEntity)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insertAllGroups(groups: List<TestGroupEntity>)
|
||||||
|
|
||||||
|
@Query("DELETE FROM testGroup")
|
||||||
|
suspend fun deleteAllGroups()
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.approagency.drug.data.local.dao
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.approagency.drug.data.local.entities.TestItemEntity
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface TestItemDao {
|
||||||
|
@Query("SELECT * FROM testItem ORDER BY Id")
|
||||||
|
fun getAllItems(): Flow<List<TestItemEntity>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM testItem WHERE Group_Id = :groupId ORDER BY Id")
|
||||||
|
fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM testItem WHERE Id = :itemId")
|
||||||
|
suspend fun getItemById(itemId: Int): TestItemEntity?
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT ti.* FROM testItem ti
|
||||||
|
INNER JOIN testGroup tg ON ti.Group_Id = tg.Id
|
||||||
|
WHERE tg.Isparent = '0' AND ti.Title LIKE '%' || :searchQuery || '%'
|
||||||
|
""")
|
||||||
|
fun searchTestItems(searchQuery: String): Flow<List<TestItemEntity>>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insertItem(item: TestItemEntity)
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
suspend fun insertAllItems(items: List<TestItemEntity>)
|
||||||
|
|
||||||
|
@Query("DELETE FROM testItem")
|
||||||
|
suspend fun deleteAllItems()
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.approagency.drug.data.local.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import com.approagency.drug.data.local.dao.TestGroupDao
|
||||||
|
import com.approagency.drug.data.local.dao.TestItemDao
|
||||||
|
import com.approagency.drug.data.local.entities.TestGroupEntity
|
||||||
|
import com.approagency.drug.data.local.entities.TestItemEntity
|
||||||
|
|
||||||
|
@Database(
|
||||||
|
entities = [
|
||||||
|
TestGroupEntity::class,
|
||||||
|
TestItemEntity::class
|
||||||
|
],
|
||||||
|
version = 1,
|
||||||
|
exportSchema = false
|
||||||
|
)
|
||||||
|
abstract class LabDatabase : RoomDatabase() {
|
||||||
|
abstract fun testGroupDao(): TestGroupDao
|
||||||
|
abstract fun testItemDao(): TestItemDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
private var INSTANCE: LabDatabase? = null
|
||||||
|
|
||||||
|
fun getInstance(context: Context): LabDatabase {
|
||||||
|
return INSTANCE ?: synchronized(this) {
|
||||||
|
val instance = Room.databaseBuilder(
|
||||||
|
context.applicationContext,
|
||||||
|
LabDatabase::class.java,
|
||||||
|
"lab_tests.db" // This will use your existing database
|
||||||
|
)
|
||||||
|
.createFromAsset("lab_tests.db") // IMPORTANT: Copy from assets
|
||||||
|
.fallbackToDestructiveMigration() // For development only
|
||||||
|
.build()
|
||||||
|
INSTANCE = instance
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.approagency.drug.data.local.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "testGroup")
|
||||||
|
data class TestGroupEntity(
|
||||||
|
@PrimaryKey
|
||||||
|
@ColumnInfo(name = "Id")
|
||||||
|
val id: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "Ename")
|
||||||
|
val ename: String?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "Fname")
|
||||||
|
val fname: String?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "Detail")
|
||||||
|
val detail: String?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "Isparent")
|
||||||
|
val isParent: String?
|
||||||
|
)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.approagency.drug.data.local.entities
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.ForeignKey
|
||||||
|
import androidx.room.Index
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(
|
||||||
|
tableName = "testItem",
|
||||||
|
// foreignKeys = [
|
||||||
|
// ForeignKey(
|
||||||
|
// entity = TestGroupEntity::class,
|
||||||
|
// parentColumns = ["Id"],
|
||||||
|
// childColumns = ["Group_Id"],
|
||||||
|
// onDelete = ForeignKey.CASCADE
|
||||||
|
// )
|
||||||
|
// ],
|
||||||
|
indices = [Index(value = ["Group_Id"])]
|
||||||
|
)
|
||||||
|
data class TestItemEntity(
|
||||||
|
@PrimaryKey
|
||||||
|
@ColumnInfo(name = "Id")
|
||||||
|
val id: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "Group_Id")
|
||||||
|
val groupId: Int,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "Title")
|
||||||
|
val title: String?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "Normal_Value")
|
||||||
|
val normalValue: String?,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "Detail")
|
||||||
|
val detail: String?
|
||||||
|
)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.approagency.drug.data.repository
|
||||||
|
|
||||||
|
import com.approagency.drug.data.local.dao.TestGroupDao
|
||||||
|
import com.approagency.drug.data.local.dao.TestItemDao
|
||||||
|
import com.approagency.drug.data.local.entities.TestGroupEntity
|
||||||
|
import com.approagency.drug.data.local.entities.TestItemEntity
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class LabRepositoryImpl (
|
||||||
|
private val testGroupDao: TestGroupDao,
|
||||||
|
private val testItemDao: TestItemDao
|
||||||
|
) {
|
||||||
|
// Test Group Operations
|
||||||
|
fun getAllGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getAllGroups()
|
||||||
|
fun getParentGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getParentGroups()
|
||||||
|
fun getChildGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getChildGroups()
|
||||||
|
suspend fun getGroupById(groupId: Int): TestGroupEntity? = testGroupDao.getGroupById(groupId)
|
||||||
|
|
||||||
|
// Test Item Operations
|
||||||
|
fun getAllItems(): Flow<List<TestItemEntity>> = testItemDao.getAllItems()
|
||||||
|
fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>> = testItemDao.getItemsByGroupId(groupId)
|
||||||
|
suspend fun getItemById(itemId: Int): TestItemEntity? = testItemDao.getItemById(itemId)
|
||||||
|
fun searchTestItems(query: String): Flow<List<TestItemEntity>> = testItemDao.searchTestItems(query)
|
||||||
|
}
|
||||||
@@ -2,20 +2,32 @@ package com.approagency.drug.di
|
|||||||
|
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import com.approagency.drug.data.local.database.LabDatabase
|
||||||
import com.approagency.drug.data.remote.DrugApiService
|
import com.approagency.drug.data.remote.DrugApiService
|
||||||
import com.approagency.drug.data.repository.DrugRepositoryImpl
|
import com.approagency.drug.data.repository.DrugRepositoryImpl
|
||||||
|
import com.approagency.drug.data.repository.LabRepositoryImpl
|
||||||
import com.approagency.drug.domain.repository.DrugRepository
|
import com.approagency.drug.domain.repository.DrugRepository
|
||||||
import com.approagency.drug.domain.usecase.GetDarmanUseCase
|
import com.approagency.drug.domain.usecase.GetDarmanUseCase
|
||||||
import com.approagency.drug.domain.usecase.GetDrugDetailUseCase
|
import com.approagency.drug.domain.usecase.GetDrugDetailUseCase
|
||||||
import com.approagency.drug.domain.usecase.GetDrugSearchUseCase
|
import com.approagency.drug.domain.usecase.GetDrugSearchUseCase
|
||||||
|
import com.approagency.drug.domain.usecase.GetTestGroupUseCase
|
||||||
|
import com.approagency.drug.domain.usecase.GetTestItemByGroupId
|
||||||
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
||||||
|
import com.approagency.drug.presentation.viewModel.LabViewModel
|
||||||
import com.approagency.drug.utils.Config
|
import com.approagency.drug.utils.Config
|
||||||
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.module.dsl.viewModel
|
import org.koin.core.module.dsl.viewModel
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
|
||||||
val appModule= module {
|
val appModule= module {
|
||||||
|
|
||||||
|
single { LabDatabase.getInstance(androidContext()) }
|
||||||
|
|
||||||
|
single { get<LabDatabase>().testGroupDao() }
|
||||||
|
single { get<LabDatabase>().testItemDao() }
|
||||||
|
|
||||||
single {
|
single {
|
||||||
Retrofit.Builder()
|
Retrofit.Builder()
|
||||||
.baseUrl(Config.BASE_URL)
|
.baseUrl(Config.BASE_URL)
|
||||||
@@ -47,8 +59,21 @@ val appModule= module {
|
|||||||
GetDarmanUseCase(get())
|
GetDarmanUseCase(get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single {
|
||||||
|
GetTestGroupUseCase(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
single {
|
||||||
|
GetTestItemByGroupId(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
single { LabRepositoryImpl(get(), get()) }
|
||||||
//view model
|
//view model
|
||||||
viewModel {
|
viewModel {
|
||||||
HomeViewModel(get() , get() , get())
|
HomeViewModel(get() , get() , get())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewModel {
|
||||||
|
LabViewModel(get() , get())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.approagency.drug.domain.model
|
||||||
|
|
||||||
|
data class TestGroup(
|
||||||
|
val id: Int,
|
||||||
|
val ename: String?,
|
||||||
|
val fname: String?,
|
||||||
|
val detail: String?,
|
||||||
|
val isParent: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package com.approagency.drug.domain.model
|
||||||
|
|
||||||
|
data class TestItem(
|
||||||
|
val id: Int,
|
||||||
|
val groupId: Int,
|
||||||
|
val title: String?,
|
||||||
|
val normalValue: String?,
|
||||||
|
val detail: String?
|
||||||
|
)
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
|
import com.approagency.drug.data.repository.LabRepositoryImpl
|
||||||
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
class GetTestGroupUseCase (
|
||||||
|
private val repository: LabRepositoryImpl
|
||||||
|
) {
|
||||||
|
operator fun invoke(): Flow<List<TestGroup>> {
|
||||||
|
return repository.getAllGroups().map { entities ->
|
||||||
|
entities.map { entity ->
|
||||||
|
TestGroup(
|
||||||
|
id = entity.id,
|
||||||
|
ename = entity.ename,
|
||||||
|
fname = entity.fname,
|
||||||
|
detail = entity.detail,
|
||||||
|
isParent = entity.isParent == "1"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
|
import com.approagency.drug.data.local.entities.TestItemEntity
|
||||||
|
import com.approagency.drug.data.repository.LabRepositoryImpl
|
||||||
|
import com.approagency.drug.domain.model.TestItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
class GetTestItemByGroupId (
|
||||||
|
private val repository: LabRepositoryImpl
|
||||||
|
) {
|
||||||
|
operator fun invoke(id:Int): Flow<List<TestItem>> {
|
||||||
|
return repository.getItemsByGroupId(groupId = id).map {
|
||||||
|
entities -> entities.map {
|
||||||
|
entity ->
|
||||||
|
TestItem(
|
||||||
|
id = entity.id,
|
||||||
|
groupId = entity.groupId,
|
||||||
|
title = entity.title,
|
||||||
|
normalValue = entity.normalValue,
|
||||||
|
detail = entity.detail,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,185 @@
|
|||||||
package com.approagency.drug.presentation.screens
|
package com.approagency.drug.presentation.screens
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
|
import com.approagency.drug.presentation.common.CustomModalDialog
|
||||||
|
import com.approagency.drug.presentation.common.Loading
|
||||||
|
import com.approagency.drug.presentation.common.PrimaryButton
|
||||||
|
import com.approagency.drug.presentation.viewModel.LabViewModel
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.count
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LabScreen(
|
fun LabScreen(
|
||||||
modifier: Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: HomeViewModel = koinViewModel()
|
viewModel: LabViewModel = koinViewModel()
|
||||||
){}
|
) {
|
||||||
|
val testGroupsState by viewModel.testGroups.collectAsState()
|
||||||
|
val isLoading = testGroupsState.isLoading
|
||||||
|
val error = testGroupsState.error
|
||||||
|
val testGroups = testGroupsState.testGroup
|
||||||
|
|
||||||
|
Column(modifier = modifier.fillMaxSize()) {
|
||||||
|
if (isLoading) {
|
||||||
|
CustomModalDialog(
|
||||||
|
onDismissRequest = { /* maybe disable dismiss while loading */ },
|
||||||
|
content = {
|
||||||
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Loading(color = MaterialTheme.colorScheme.primary)
|
||||||
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
|
||||||
|
Text("در حال جستجو..." , textAlign = TextAlign.Right)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// modifier = Modifier
|
||||||
|
// .matchParentSize()
|
||||||
|
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if(error != null){
|
||||||
|
CustomBox(
|
||||||
|
child = {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
modifier = Modifier.padding(MaterialTheme.dime.md)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "خطا در دریافت اطلاعات",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = error,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
modifier = Modifier.padding(vertical = 8.dp)
|
||||||
|
)
|
||||||
|
PrimaryButton(
|
||||||
|
text = "تلاش مجدد",
|
||||||
|
height = 40,
|
||||||
|
isLoading = false,
|
||||||
|
onClick = { viewModel.loadTestGroups() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else if (testGroups != null){
|
||||||
|
TestGroupsList(
|
||||||
|
testGroupsFlow = testGroups,
|
||||||
|
viewModel = viewModel
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text("هیچ دادهای موجود نیست")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TestGroupsList(
|
||||||
|
testGroupsFlow: Flow<List<TestGroup>>,
|
||||||
|
viewModel: LabViewModel
|
||||||
|
) {
|
||||||
|
val testGroups by testGroupsFlow.collectAsState(initial = emptyList())
|
||||||
|
|
||||||
|
if (testGroups.isEmpty()) {
|
||||||
|
CustomModalDialog(
|
||||||
|
onDismissRequest = { /* maybe disable dismiss while loading */ },
|
||||||
|
content = {
|
||||||
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Loading(color = MaterialTheme.colorScheme.primary)
|
||||||
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
|
||||||
|
Text("در حال جستجو..." , textAlign = TextAlign.Right)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// modifier = Modifier
|
||||||
|
// .matchParentSize()
|
||||||
|
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
items(testGroups) { group ->
|
||||||
|
TestGroupItem(
|
||||||
|
group = group,
|
||||||
|
onClick = {
|
||||||
|
// Navigate to test items of this group
|
||||||
|
// viewModel.loadTestItemsByGroup(group.id)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TestGroupItem(
|
||||||
|
group: TestGroup,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
CustomBox (
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(bottom = MaterialTheme.dime.sm)
|
||||||
|
.clickable(
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) {
|
||||||
|
onClick()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.Start
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = group.fname ?: group.ename ?: "بدون نام",
|
||||||
|
style = MaterialTheme.typography.titleMedium.copy(
|
||||||
|
textAlign = TextAlign.Right,
|
||||||
|
textDirection = TextDirection.Rtl
|
||||||
|
),
|
||||||
|
modifier = Modifier.padding(vertical = MaterialTheme.dime.xs)
|
||||||
|
)
|
||||||
|
group.detail?.let {
|
||||||
|
Text(
|
||||||
|
text = it,
|
||||||
|
style = MaterialTheme.typography.bodySmall.copy(
|
||||||
|
textAlign = TextAlign.Right ,
|
||||||
|
textDirection = TextDirection.Rtl
|
||||||
|
),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(horizontal = MaterialTheme.dime.lg)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -80,7 +80,7 @@ fun RootScreen(navHostController: NavHostController, modifier: Modifier){
|
|||||||
containerColor = MaterialTheme.colorScheme.surface,
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(110.dp).padding(bottom = 2.dp).clip(MaterialTheme.shapes.extraLarge), // 👈 کوچیکتر از حالت پیشفرض
|
.height(110.dp).padding(bottom = 2.dp).clip(MaterialTheme.shapes.extraLarge),
|
||||||
tonalElevation = 8.dp, // subtle shadow
|
tonalElevation = 8.dp, // subtle shadow
|
||||||
windowInsets = WindowInsets.navigationBars
|
windowInsets = WindowInsets.navigationBars
|
||||||
.only(WindowInsetsSides.Bottom)
|
.only(WindowInsetsSides.Bottom)
|
||||||
@@ -114,7 +114,7 @@ fun RootScreen(navHostController: NavHostController, modifier: Modifier){
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
alwaysShowLabel = true // 👈 فقط وقتی select شد label نشون بده
|
alwaysShowLabel = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package com.approagency.drug.presentation.viewModel
|
||||||
|
|
||||||
|
import android.net.http.HttpException
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
|
import com.approagency.drug.domain.model.TestItem
|
||||||
|
import com.approagency.drug.domain.usecase.GetTestGroupUseCase
|
||||||
|
import com.approagency.drug.domain.usecase.GetTestItemByGroupId
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
class LabViewModel (
|
||||||
|
private val getTestGroupUseCase: GetTestGroupUseCase,
|
||||||
|
private val getTestItemByGroupId: GetTestItemByGroupId
|
||||||
|
) : ViewModel(){
|
||||||
|
private val _testGroups = MutableStateFlow(TestGroupItem())
|
||||||
|
val testGroups: StateFlow<TestGroupItem> = _testGroups.asStateFlow()
|
||||||
|
|
||||||
|
private val _testItems = MutableStateFlow(TestItemState())
|
||||||
|
val testItems: StateFlow<TestItemState> = _testItems.asStateFlow()
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadTestGroups()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun loadTestGroups(){
|
||||||
|
_testGroups.update {
|
||||||
|
it.copy(
|
||||||
|
isLoading = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val result = getTestGroupUseCase.invoke();
|
||||||
|
_testGroups.update {
|
||||||
|
it.copy(
|
||||||
|
isLoading = false,
|
||||||
|
testGroup = result,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e:HttpException){
|
||||||
|
handleError(e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getItemByGroupId(id:Int){
|
||||||
|
_testItems.update { it.copy(isLoading = true) }
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val result = getTestItemByGroupId.invoke(id);
|
||||||
|
_testItems.update {
|
||||||
|
it.copy(
|
||||||
|
isLoading = false,
|
||||||
|
testItem = result
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e:HttpException){
|
||||||
|
handleError(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private suspend fun handleError(message: String?) {
|
||||||
|
_testGroups.update {
|
||||||
|
it.copy(
|
||||||
|
isLoading = false,
|
||||||
|
testGroup = null,
|
||||||
|
error = message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
data class TestGroupItem(
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val testGroup: Flow<List<TestGroup>>? = null,
|
||||||
|
val error:String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class TestItemState(
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val testItem: Flow<List<TestItem>>? = null,
|
||||||
|
val error:String? = null
|
||||||
|
)
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.13.0"
|
agp = "8.13.0"
|
||||||
kotlin = "2.2.10"
|
kotlin = "2.0.21"
|
||||||
coreKtx = "1.10.1"
|
coreKtx = "1.10.1"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
junitVersion = "1.1.5"
|
junitVersion = "1.1.5"
|
||||||
@@ -8,16 +8,16 @@ espressoCore = "3.5.1"
|
|||||||
lifecycleRuntimeKtx = "2.6.1"
|
lifecycleRuntimeKtx = "2.6.1"
|
||||||
activityCompose = "1.8.0"
|
activityCompose = "1.8.0"
|
||||||
composeBom = "2024.09.00"
|
composeBom = "2024.09.00"
|
||||||
|
ksp = "2.0.21-1.0.27"
|
||||||
retrofit = "3.0.0"
|
retrofit = "2.11.0"
|
||||||
okhttp = "5.1.0"
|
okhttp = "4.12.0"
|
||||||
gson = "2.13.1"
|
gson = "2.13.1"
|
||||||
coil = "2.7.0"
|
coil = "2.7.0"
|
||||||
location = "21.3.0"
|
location = "21.3.0"
|
||||||
koinAndroid = "4.1.0"
|
koinAndroid = "4.1.0"
|
||||||
koinAndroidxCompose = "4.1.0"
|
koinAndroidxCompose = "4.1.0"
|
||||||
lifecycleViewmodelKtx = "2.9.4"
|
lifecycleViewmodelKtx = "2.9.4"
|
||||||
|
room = "2.6.1"
|
||||||
navigationCompose = "2.8.5"
|
navigationCompose = "2.8.5"
|
||||||
material3 = "1.4.0"
|
material3 = "1.4.0"
|
||||||
|
|
||||||
@@ -51,8 +51,14 @@ location-services = { group = "com.google.android.gms", name = "play-services-lo
|
|||||||
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||||
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
|
||||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
|
||||||
|
|
||||||
|
|
||||||
|
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||||
|
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||||
|
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
||||||
|
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
|
||||||
Reference in New Issue
Block a user