feat: add test items and group tests
This commit is contained in:
Generated
+3
-3
@@ -1,3 +1,3 @@
|
|||||||
# Default ignored files
|
# Default ignored files
|
||||||
/shelf/
|
/shelf/
|
||||||
/workspace.xml
|
/workspace.xml
|
||||||
|
|||||||
Generated
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="AndroidProjectSystem">
|
<component name="AndroidProjectSystem">
|
||||||
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
Generated
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="CompilerConfiguration">
|
<component name="CompilerConfiguration">
|
||||||
<bytecodeTargetLevel target="21" />
|
<bytecodeTargetLevel target="17" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
Generated
+9
-9
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="deploymentTargetSelector">
|
<component name="deploymentTargetSelector">
|
||||||
<selectionStates>
|
<selectionStates>
|
||||||
<SelectionState runConfigName="app">
|
<SelectionState runConfigName="app">
|
||||||
<option name="selectionMode" value="DROPDOWN" />
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
</SelectionState>
|
</SelectionState>
|
||||||
</selectionStates>
|
</selectionStates>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
Generated
+18
-18
@@ -1,19 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
<option name="testRunner" value="CHOOSE_PER_TEST" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||||
<option name="modules">
|
<option name="modules">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
<option value="$PROJECT_DIR$/app" />
|
<option value="$PROJECT_DIR$/app" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</GradleProjectSettings>
|
</GradleProjectSettings>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
Generated
+9
-9
@@ -1,10 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectMigrations">
|
<component name="ProjectMigrations">
|
||||||
<option name="MigrateToGradleLocalJavaHome">
|
<option name="MigrateToGradleLocalJavaHome">
|
||||||
<set>
|
<set>
|
||||||
<option value="$PROJECT_DIR$" />
|
<option value="$PROJECT_DIR$" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
Generated
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectType">
|
<component name="ProjectType">
|
||||||
|
|||||||
Generated
+16
-16
@@ -1,17 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="RunConfigurationProducerService">
|
<component name="RunConfigurationProducerService">
|
||||||
<option name="ignoredProducers">
|
<option name="ignoredProducers">
|
||||||
<set>
|
<set>
|
||||||
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
|
||||||
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
|
||||||
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
|
||||||
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
|
||||||
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
|
||||||
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
|
||||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
|
||||||
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
|
||||||
</set>
|
</set>
|
||||||
</option>
|
</option>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
Generated
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="VcsDirectoryMappings">
|
<component name="VcsDirectoryMappings">
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -2,44 +2,44 @@ kotlin version: 2.0.21
|
|||||||
error message: Failed connecting to the daemon in 4 retries
|
error message: Failed connecting to the daemon in 4 retries
|
||||||
|
|
||||||
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
||||||
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
||||||
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
||||||
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
||||||
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
||||||
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
||||||
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
||||||
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
||||||
at java.base/java.lang.Thread.run(Unknown Source)
|
at java.base/java.lang.Thread.run(Unknown Source)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,44 +2,44 @@ kotlin version: 2.0.21
|
|||||||
error message: Failed connecting to the daemon in 4 retries
|
error message: Failed connecting to the daemon in 4 retries
|
||||||
|
|
||||||
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
||||||
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
||||||
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
||||||
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
||||||
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
||||||
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
||||||
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
||||||
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
||||||
at java.base/java.lang.Thread.run(Unknown Source)
|
at java.base/java.lang.Thread.run(Unknown Source)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,44 +2,44 @@ kotlin version: 2.0.21
|
|||||||
error message: Failed connecting to the daemon in 4 retries
|
error message: Failed connecting to the daemon in 4 retries
|
||||||
|
|
||||||
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
||||||
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
||||||
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
||||||
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
||||||
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
||||||
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
||||||
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
||||||
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
||||||
at java.base/java.lang.Thread.run(Unknown Source)
|
at java.base/java.lang.Thread.run(Unknown Source)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,44 +2,44 @@ kotlin version: 2.0.21
|
|||||||
error message: Failed connecting to the daemon in 4 retries
|
error message: Failed connecting to the daemon in 4 retries
|
||||||
|
|
||||||
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
||||||
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
||||||
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
||||||
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
||||||
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
||||||
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
||||||
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
||||||
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
||||||
at java.base/java.lang.Thread.run(Unknown Source)
|
at java.base/java.lang.Thread.run(Unknown Source)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,44 +2,44 @@ kotlin version: 2.0.21
|
|||||||
error message: Failed connecting to the daemon in 4 retries
|
error message: Failed connecting to the daemon in 4 retries
|
||||||
|
|
||||||
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
|
||||||
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
|
||||||
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
|
||||||
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
|
||||||
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
||||||
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
||||||
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
|
||||||
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
|
||||||
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
|
||||||
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
at org.gradle.internal.Factories$1.create(Factories.java:31)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
|
||||||
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
|
||||||
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
|
||||||
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
|
||||||
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
|
||||||
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
||||||
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
||||||
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
||||||
at java.base/java.lang.Thread.run(Unknown Source)
|
at java.base/java.lang.Thread.run(Unknown Source)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,11 +31,11 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "11"
|
jvmTarget = "17"
|
||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
<?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" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<application
|
<application
|
||||||
android:name=".DrugApp"
|
android:name=".DrugApp"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Drug">
|
android:theme="@style/Theme.Drug">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/Theme.Drug">
|
android:theme="@style/Theme.Drug">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="preloaded_fonts"
|
android:name="preloaded_fonts"
|
||||||
android:resource="@array/preloaded_fonts" />
|
android:resource="@array/preloaded_fonts" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
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.data.local.database.LabDatabase
|
||||||
import com.approagency.drug.di.appModule
|
import com.approagency.drug.di.appModule
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
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
|
||||||
|
|
||||||
class DrugApp : Application(){
|
class DrugApp : Application(){
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
startKoin {
|
startKoin {
|
||||||
androidContext(this@DrugApp)
|
androidContext(this@DrugApp)
|
||||||
modules(appModule)
|
modules(appModule)
|
||||||
printLogger()
|
printLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preload database (optional, for faster access)
|
// Preload database (optional, for faster access)
|
||||||
GlobalScope.launch {
|
GlobalScope.launch {
|
||||||
LabDatabase.getInstance(this@DrugApp)
|
LabDatabase.getInstance(this@DrugApp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.approagency.drug.data.dto
|
package com.approagency.drug.data.dto
|
||||||
|
|
||||||
data class DarmanModel(
|
data class DarmanModel(
|
||||||
val success : String,
|
val success : String,
|
||||||
val message:String,
|
val message:String,
|
||||||
val data: List<DarmanList>
|
val data: List<DarmanList>
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DarmanList(
|
data class DarmanList(
|
||||||
val cod : Int,
|
val cod : Int,
|
||||||
val nam_fa:String,
|
val nam_fa:String,
|
||||||
val nam_en:String,
|
val nam_en:String,
|
||||||
)
|
)
|
||||||
@@ -1,74 +1,74 @@
|
|||||||
package com.approagency.drug.data.dto
|
package com.approagency.drug.data.dto
|
||||||
|
|
||||||
data class DrugModels(
|
data class DrugModels(
|
||||||
val success: Boolean,
|
val success: Boolean,
|
||||||
val message: String?,
|
val message: String?,
|
||||||
val data: DrugDetail?,
|
val data: DrugDetail?,
|
||||||
val meta: Meta?
|
val meta: Meta?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class DrugDetail(
|
data class DrugDetail(
|
||||||
val cod: Int?,
|
val cod: Int?,
|
||||||
val goroh_darmani_detail_cod: Int?,
|
val goroh_darmani_detail_cod: Int?,
|
||||||
val goroh_daroei_cod: Int?,
|
val goroh_daroei_cod: Int?,
|
||||||
val goroh_farmakologic_cod: Int?,
|
val goroh_farmakologic_cod: Int?,
|
||||||
val goroh_darmani_cod: Int?,
|
val goroh_darmani_cod: Int?,
|
||||||
val nam_fa: String?,
|
val nam_fa: String?,
|
||||||
val nam_en: String?,
|
val nam_en: String?,
|
||||||
val mavaredmasraf: String?,
|
val mavaredmasraf: String?,
|
||||||
val meghdarmasraf: String?,
|
val meghdarmasraf: String?,
|
||||||
val masrafdarhamelegi: String?,
|
val masrafdarhamelegi: String?,
|
||||||
val masrafdarshirdehi: String?,
|
val masrafdarshirdehi: String?,
|
||||||
val manemasraf: String?,
|
val manemasraf: String?,
|
||||||
val avarez: String?,
|
val avarez: String?,
|
||||||
val tadakhol: String?,
|
val tadakhol: String?,
|
||||||
val mekanismtasir: String?,
|
val mekanismtasir: String?,
|
||||||
val nokte: String?,
|
val nokte: String?,
|
||||||
val hoshdar: String?,
|
val hoshdar: String?,
|
||||||
val sharayetnegahdari: String?,
|
val sharayetnegahdari: String?,
|
||||||
val ashkal_daroei: String?,
|
val ashkal_daroei: String?,
|
||||||
val created_at: String?,
|
val created_at: String?,
|
||||||
val updated_at: String?,
|
val updated_at: String?,
|
||||||
val goroh_daroei: GorohDaroei?,
|
val goroh_daroei: GorohDaroei?,
|
||||||
val goroh_darmani: GorohDarmani?,
|
val goroh_darmani: GorohDarmani?,
|
||||||
val goroh_darmani_detail: GorohDarmaniDetail?
|
val goroh_darmani_detail: GorohDarmaniDetail?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class GorohDaroei(
|
data class GorohDaroei(
|
||||||
val cod: Int?,
|
val cod: Int?,
|
||||||
val nam: String?,
|
val nam: String?,
|
||||||
val created_at: String?,
|
val created_at: String?,
|
||||||
val updated_at: String?
|
val updated_at: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class GorohDarmani(
|
data class GorohDarmani(
|
||||||
val cod: Int?,
|
val cod: Int?,
|
||||||
val nam_fa: String?,
|
val nam_fa: String?,
|
||||||
val nam_en: String?,
|
val nam_en: String?,
|
||||||
val giyahi_ya_shimiyaei: String?,
|
val giyahi_ya_shimiyaei: String?,
|
||||||
val image: String?,
|
val image: String?,
|
||||||
val created_at: String?,
|
val created_at: String?,
|
||||||
val updated_at: String?
|
val updated_at: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class GorohDarmaniDetail(
|
data class GorohDarmaniDetail(
|
||||||
val cod: Int?,
|
val cod: Int?,
|
||||||
val nam: String?,
|
val nam: String?,
|
||||||
val created_at: String?,
|
val created_at: String?,
|
||||||
val updated_at: String?
|
val updated_at: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Meta(
|
data class Meta(
|
||||||
val current_page: Int?,
|
val current_page: Int?,
|
||||||
val last_page: Int?,
|
val last_page: Int?,
|
||||||
val per_page: Int?,
|
val per_page: Int?,
|
||||||
val total: Int?
|
val total: Int?
|
||||||
)
|
)
|
||||||
|
|
||||||
// For list response compatibility
|
// For list response compatibility
|
||||||
data class DrugListResponse(
|
data class DrugListResponse(
|
||||||
val success: Boolean,
|
val success: Boolean,
|
||||||
val message: String?,
|
val message: String?,
|
||||||
val data: List<DrugDetail> = emptyList(),
|
val data: List<DrugDetail> = emptyList(),
|
||||||
val meta: Meta?
|
val meta: Meta?
|
||||||
)
|
)
|
||||||
@@ -1,32 +1,46 @@
|
|||||||
package com.approagency.drug.data.local.dao
|
package com.approagency.drug.data.local.dao
|
||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import com.approagency.drug.data.local.entities.TestGroupEntity
|
import com.approagency.drug.data.local.entities.TestGroupEntity
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface TestGroupDao {
|
interface TestGroupDao {
|
||||||
@Query("SELECT * FROM testGroup ORDER BY Id")
|
@Query("SELECT * FROM testGroup ORDER BY Id")
|
||||||
fun getAllGroups(): Flow<List<TestGroupEntity>>
|
fun getAllGroups(): Flow<List<TestGroupEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM testGroup WHERE Id = :groupId")
|
@Query("SELECT * FROM testGroup WHERE Id = :groupId")
|
||||||
suspend fun getGroupById(groupId: Int): TestGroupEntity?
|
suspend fun getGroupById(groupId: Int): TestGroupEntity?
|
||||||
|
|
||||||
@Query("SELECT * FROM testGroup WHERE Isparent = '1'")
|
@Query("SELECT * FROM testGroup WHERE Isparent = '1'")
|
||||||
fun getParentGroups(): Flow<List<TestGroupEntity>>
|
fun getParentGroups(): Flow<List<TestGroupEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM testGroup WHERE Isparent = '0'")
|
@Query("SELECT * FROM testGroup WHERE Isparent = '0'")
|
||||||
fun getChildGroups(): Flow<List<TestGroupEntity>>
|
fun getChildGroups(): Flow<List<TestGroupEntity>>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun insertGroup(group: TestGroupEntity)
|
suspend fun insertGroup(group: TestGroupEntity)
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun insertAllGroups(groups: List<TestGroupEntity>)
|
suspend fun insertAllGroups(groups: List<TestGroupEntity>)
|
||||||
|
|
||||||
@Query("DELETE FROM testGroup")
|
@Query("DELETE FROM testGroup")
|
||||||
suspend fun deleteAllGroups()
|
suspend fun deleteAllGroups()
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT * FROM testGroup
|
||||||
|
WHERE Fname LIKE '%' || :query || '%'
|
||||||
|
OR Ename LIKE '%' || :query || '%'
|
||||||
|
OR Detail LIKE '%' || :query || '%'
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN Fname LIKE '%' || :query || '%' THEN 1
|
||||||
|
WHEN Ename LIKE '%' || :query || '%' THEN 2
|
||||||
|
ELSE 3
|
||||||
|
END
|
||||||
|
""")
|
||||||
|
fun searchGroups(query: String): Flow<List<TestGroupEntity>>
|
||||||
}
|
}
|
||||||
@@ -1,36 +1,46 @@
|
|||||||
package com.approagency.drug.data.local.dao
|
package com.approagency.drug.data.local.dao
|
||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import com.approagency.drug.data.local.entities.TestItemEntity
|
import com.approagency.drug.data.local.entities.TestItemEntity
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
interface TestItemDao {
|
interface TestItemDao {
|
||||||
@Query("SELECT * FROM testItem ORDER BY Id")
|
@Query("SELECT * FROM testItem ORDER BY Id")
|
||||||
fun getAllItems(): Flow<List<TestItemEntity>>
|
fun getAllItems(): Flow<List<TestItemEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM testItem WHERE Group_Id = :groupId ORDER BY Id")
|
@Query("SELECT * FROM testItem WHERE Group_Id = :groupId ORDER BY Id")
|
||||||
fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>>
|
fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>>
|
||||||
|
|
||||||
@Query("SELECT * FROM testItem WHERE Id = :itemId")
|
@Query("SELECT * FROM testItem WHERE Id = :itemId")
|
||||||
suspend fun getItemById(itemId: Int): TestItemEntity?
|
suspend fun getItemById(itemId: Int): TestItemEntity?
|
||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT ti.* FROM testItem ti
|
SELECT ti.* FROM testItem ti
|
||||||
INNER JOIN testGroup tg ON ti.Group_Id = tg.Id
|
LEFT JOIN testGroup tg ON ti.Group_Id = tg.Id
|
||||||
WHERE tg.Isparent = '0' AND ti.Title LIKE '%' || :searchQuery || '%'
|
WHERE ti.Title LIKE '%' || :searchQuery || '%'
|
||||||
""")
|
OR ti.Normal_Value LIKE '%' || :searchQuery || '%'
|
||||||
fun searchTestItems(searchQuery: String): Flow<List<TestItemEntity>>
|
OR ti.Detail LIKE '%' || :searchQuery || '%'
|
||||||
|
OR tg.Fname LIKE '%' || :searchQuery || '%'
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
OR tg.Ename LIKE '%' || :searchQuery || '%'
|
||||||
suspend fun insertItem(item: TestItemEntity)
|
ORDER BY
|
||||||
|
CASE
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
WHEN ti.Title LIKE '%' || :searchQuery || '%' THEN 1
|
||||||
suspend fun insertAllItems(items: List<TestItemEntity>)
|
WHEN tg.Fname LIKE '%' || :searchQuery || '%' THEN 2
|
||||||
|
ELSE 3
|
||||||
@Query("DELETE FROM testItem")
|
END
|
||||||
suspend fun deleteAllItems()
|
""")
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
@@ -1,43 +1,43 @@
|
|||||||
package com.approagency.drug.data.local.database
|
package com.approagency.drug.data.local.database
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.room.Database
|
import androidx.room.Database
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.room.RoomDatabase
|
import androidx.room.RoomDatabase
|
||||||
import com.approagency.drug.data.local.dao.TestGroupDao
|
import com.approagency.drug.data.local.dao.TestGroupDao
|
||||||
import com.approagency.drug.data.local.dao.TestItemDao
|
import com.approagency.drug.data.local.dao.TestItemDao
|
||||||
import com.approagency.drug.data.local.entities.TestGroupEntity
|
import com.approagency.drug.data.local.entities.TestGroupEntity
|
||||||
import com.approagency.drug.data.local.entities.TestItemEntity
|
import com.approagency.drug.data.local.entities.TestItemEntity
|
||||||
|
|
||||||
@Database(
|
@Database(
|
||||||
entities = [
|
entities = [
|
||||||
TestGroupEntity::class,
|
TestGroupEntity::class,
|
||||||
TestItemEntity::class
|
TestItemEntity::class
|
||||||
],
|
],
|
||||||
version = 1,
|
version = 1,
|
||||||
exportSchema = false
|
exportSchema = false
|
||||||
)
|
)
|
||||||
abstract class LabDatabase : RoomDatabase() {
|
abstract class LabDatabase : RoomDatabase() {
|
||||||
abstract fun testGroupDao(): TestGroupDao
|
abstract fun testGroupDao(): TestGroupDao
|
||||||
abstract fun testItemDao(): TestItemDao
|
abstract fun testItemDao(): TestItemDao
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@Volatile
|
@Volatile
|
||||||
private var INSTANCE: LabDatabase? = null
|
private var INSTANCE: LabDatabase? = null
|
||||||
|
|
||||||
fun getInstance(context: Context): LabDatabase {
|
fun getInstance(context: Context): LabDatabase {
|
||||||
return INSTANCE ?: synchronized(this) {
|
return INSTANCE ?: synchronized(this) {
|
||||||
val instance = Room.databaseBuilder(
|
val instance = Room.databaseBuilder(
|
||||||
context.applicationContext,
|
context.applicationContext,
|
||||||
LabDatabase::class.java,
|
LabDatabase::class.java,
|
||||||
"lab_tests.db" // This will use your existing database
|
"lab_tests.db" // This will use your existing database
|
||||||
)
|
)
|
||||||
.createFromAsset("lab_tests.db") // IMPORTANT: Copy from assets
|
.createFromAsset("lab_tests.db") // IMPORTANT: Copy from assets
|
||||||
.fallbackToDestructiveMigration() // For development only
|
.fallbackToDestructiveMigration() // For development only
|
||||||
.build()
|
.build()
|
||||||
INSTANCE = instance
|
INSTANCE = instance
|
||||||
instance
|
instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
package com.approagency.drug.data.local.entities
|
package com.approagency.drug.data.local.entities
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(tableName = "testGroup")
|
@Entity(tableName = "testGroup")
|
||||||
data class TestGroupEntity(
|
data class TestGroupEntity(
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
@ColumnInfo(name = "Id")
|
@ColumnInfo(name = "Id")
|
||||||
val id: Int,
|
val id: Int,
|
||||||
|
|
||||||
@ColumnInfo(name = "Ename")
|
@ColumnInfo(name = "Ename")
|
||||||
val ename: String?,
|
val ename: String?,
|
||||||
|
|
||||||
@ColumnInfo(name = "Fname")
|
@ColumnInfo(name = "Fname")
|
||||||
val fname: String?,
|
val fname: String?,
|
||||||
|
|
||||||
@ColumnInfo(name = "Detail")
|
@ColumnInfo(name = "Detail")
|
||||||
val detail: String?,
|
val detail: String?,
|
||||||
|
|
||||||
@ColumnInfo(name = "Isparent")
|
@ColumnInfo(name = "Isparent")
|
||||||
val isParent: String?
|
val isParent: String?
|
||||||
)
|
)
|
||||||
@@ -1,37 +1,37 @@
|
|||||||
package com.approagency.drug.data.local.entities
|
package com.approagency.drug.data.local.entities
|
||||||
|
|
||||||
import androidx.room.ColumnInfo
|
import androidx.room.ColumnInfo
|
||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.ForeignKey
|
import androidx.room.ForeignKey
|
||||||
import androidx.room.Index
|
import androidx.room.Index
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
@Entity(
|
@Entity(
|
||||||
tableName = "testItem",
|
tableName = "testItem",
|
||||||
// foreignKeys = [
|
// foreignKeys = [
|
||||||
// ForeignKey(
|
// ForeignKey(
|
||||||
// entity = TestGroupEntity::class,
|
// entity = TestGroupEntity::class,
|
||||||
// parentColumns = ["Id"],
|
// parentColumns = ["Id"],
|
||||||
// childColumns = ["Group_Id"],
|
// childColumns = ["Group_Id"],
|
||||||
// onDelete = ForeignKey.CASCADE
|
// onDelete = ForeignKey.CASCADE
|
||||||
// )
|
// )
|
||||||
// ],
|
// ],
|
||||||
indices = [Index(value = ["Group_Id"])]
|
indices = [Index(value = ["Group_Id"])]
|
||||||
)
|
)
|
||||||
data class TestItemEntity(
|
data class TestItemEntity(
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
@ColumnInfo(name = "Id")
|
@ColumnInfo(name = "Id")
|
||||||
val id: Int,
|
val id: Int,
|
||||||
|
|
||||||
@ColumnInfo(name = "Group_Id")
|
@ColumnInfo(name = "Group_Id")
|
||||||
val groupId: Int,
|
val groupId: Int,
|
||||||
|
|
||||||
@ColumnInfo(name = "Title")
|
@ColumnInfo(name = "Title")
|
||||||
val title: String?,
|
val title: String?,
|
||||||
|
|
||||||
@ColumnInfo(name = "Normal_Value")
|
@ColumnInfo(name = "Normal_Value")
|
||||||
val normalValue: String?,
|
val normalValue: String?,
|
||||||
|
|
||||||
@ColumnInfo(name = "Detail")
|
@ColumnInfo(name = "Detail")
|
||||||
val detail: String?
|
val detail: String?
|
||||||
)
|
)
|
||||||
@@ -1,29 +1,29 @@
|
|||||||
package com.approagency.drug.data.remote
|
package com.approagency.drug.data.remote
|
||||||
|
|
||||||
import com.approagency.drug.data.dto.DarmanModel
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.data.dto.DrugModels
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
import retrofit2.http.Path
|
import retrofit2.http.Path
|
||||||
import retrofit2.http.Query
|
import retrofit2.http.Query
|
||||||
|
|
||||||
interface DrugApiService {
|
interface DrugApiService {
|
||||||
@GET("drugs/search")
|
@GET("drugs/search")
|
||||||
suspend fun getDrugs(
|
suspend fun getDrugs(
|
||||||
@Query("q")query: String?,
|
@Query("q")query: String?,
|
||||||
@Query("per_page")perPage:Int? = 20,
|
@Query("per_page")perPage:Int? = 20,
|
||||||
@Query("with_relations")withRelations: Boolean? =true,
|
@Query("with_relations")withRelations: Boolean? =true,
|
||||||
@Query("goroh_daroei_cod")drugGroup:Int?,
|
@Query("goroh_daroei_cod")drugGroup:Int?,
|
||||||
@Query("goroh_darmani_cod")healGroup:Int?,
|
@Query("goroh_darmani_cod")healGroup:Int?,
|
||||||
): DrugListResponse
|
): DrugListResponse
|
||||||
|
|
||||||
|
|
||||||
@GET("drugs/{cod}")
|
@GET("drugs/{cod}")
|
||||||
suspend fun getDrugDetail(
|
suspend fun getDrugDetail(
|
||||||
@Path("cod") cod: Int
|
@Path("cod") cod: Int
|
||||||
): DrugModels
|
): DrugModels
|
||||||
|
|
||||||
|
|
||||||
@GET("drugs/goroh-darmani")
|
@GET("drugs/goroh-darmani")
|
||||||
suspend fun getGorohDaroei(): DarmanModel
|
suspend fun getGorohDaroei(): DarmanModel
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,39 @@
|
|||||||
package com.approagency.drug.data.repository
|
package com.approagency.drug.data.repository
|
||||||
|
|
||||||
import com.approagency.drug.data.dto.DarmanModel
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.data.dto.DrugModels
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import com.approagency.drug.data.remote.DrugApiService
|
import com.approagency.drug.data.remote.DrugApiService
|
||||||
import com.approagency.drug.domain.model.DrugSearchParams
|
import com.approagency.drug.domain.model.DrugSearchParams
|
||||||
import com.approagency.drug.domain.repository.DrugRepository
|
import com.approagency.drug.domain.repository.DrugRepository
|
||||||
|
|
||||||
class DrugRepositoryImpl(
|
class DrugRepositoryImpl(
|
||||||
private val apiService: DrugApiService
|
private val apiService: DrugApiService
|
||||||
): DrugRepository {
|
): DrugRepository {
|
||||||
override suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse> {
|
override suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse> {
|
||||||
return try {
|
return try {
|
||||||
val response = apiService.getDrugs(query = params.query , perPage = params.perPage , withRelations = params.withRelations , healGroup = params.healGroup , drugGroup = params.drugGroup )
|
val response = apiService.getDrugs(query = params.query , perPage = params.perPage , withRelations = params.withRelations , healGroup = params.healGroup , drugGroup = params.drugGroup )
|
||||||
return Result.success(response)
|
return Result.success(response)
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun drugDetail(cod: Int): Result<DrugModels> {
|
override suspend fun drugDetail(cod: Int): Result<DrugModels> {
|
||||||
return try {
|
return try {
|
||||||
val response= apiService.getDrugDetail(cod = cod)
|
val response= apiService.getDrugDetail(cod = cod)
|
||||||
return Result.success(response)
|
return Result.success(response)
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getGorohDaroei(): Result<DarmanModel> {
|
override suspend fun getGorohDaroei(): Result<DarmanModel> {
|
||||||
return try {
|
return try {
|
||||||
val response = apiService.getGorohDaroei()
|
val response = apiService.getGorohDaroei()
|
||||||
return Result.success(response)
|
return Result.success(response)
|
||||||
}catch (e: Exception){
|
}catch (e: Exception){
|
||||||
Result.failure(e)
|
Result.failure(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,35 @@
|
|||||||
package com.approagency.drug.data.repository
|
package com.approagency.drug.data.repository
|
||||||
|
|
||||||
import com.approagency.drug.data.local.dao.TestGroupDao
|
import com.approagency.drug.data.local.dao.TestGroupDao
|
||||||
import com.approagency.drug.data.local.dao.TestItemDao
|
import com.approagency.drug.data.local.dao.TestItemDao
|
||||||
import com.approagency.drug.data.local.entities.TestGroupEntity
|
import com.approagency.drug.data.local.entities.TestGroupEntity
|
||||||
import com.approagency.drug.data.local.entities.TestItemEntity
|
import com.approagency.drug.data.local.entities.TestItemEntity
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
class LabRepositoryImpl (
|
|
||||||
private val testGroupDao: TestGroupDao,
|
class LabRepositoryImpl (
|
||||||
private val testItemDao: TestItemDao
|
private val testGroupDao: TestGroupDao,
|
||||||
) {
|
private val testItemDao: TestItemDao
|
||||||
// Test Group Operations
|
) {
|
||||||
fun getAllGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getAllGroups()
|
// Test Group Operations
|
||||||
fun getParentGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getParentGroups()
|
fun getAllGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getAllGroups()
|
||||||
fun getChildGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getChildGroups()
|
fun getParentGroups(): Flow<List<TestGroupEntity>> = testGroupDao.getParentGroups()
|
||||||
suspend fun getGroupById(groupId: Int): TestGroupEntity? = testGroupDao.getGroupById(groupId)
|
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()
|
// Test Item Operations
|
||||||
fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>> = testItemDao.getItemsByGroupId(groupId)
|
fun getAllItems(): Flow<List<TestItemEntity>> = testItemDao.getAllItems()
|
||||||
suspend fun getItemById(itemId: Int): TestItemEntity? = testItemDao.getItemById(itemId)
|
fun getItemsByGroupId(groupId: Int): Flow<List<TestItemEntity>> = testItemDao.getItemsByGroupId(groupId)
|
||||||
fun searchTestItems(query: String): Flow<List<TestItemEntity>> = testItemDao.searchTestItems(query)
|
suspend fun getItemById(itemId: Int): TestItemEntity? = testItemDao.getItemById(itemId)
|
||||||
|
fun searchTestItems(query: String): Flow<List<TestItemEntity>> = testItemDao.searchTestItems(query)
|
||||||
|
|
||||||
|
|
||||||
|
fun searchGroupsAndItems(searchQuery: String): Flow<Pair<List<TestGroupEntity>, List<TestItemEntity>>> {
|
||||||
|
return combine(
|
||||||
|
testGroupDao.searchGroups(searchQuery),
|
||||||
|
testItemDao.searchTestItems(searchQuery)
|
||||||
|
) { groups, items ->
|
||||||
|
Pair(groups, items)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,79 +1,82 @@
|
|||||||
package com.approagency.drug.di
|
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.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.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.GetTestGroupUseCase
|
||||||
import com.approagency.drug.domain.usecase.GetTestItemByGroupId
|
import com.approagency.drug.domain.usecase.GetTestItemByGroupId
|
||||||
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
import com.approagency.drug.domain.usecase.SearchTestsUseCase
|
||||||
import com.approagency.drug.presentation.viewModel.LabViewModel
|
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
||||||
import com.approagency.drug.utils.Config
|
import com.approagency.drug.presentation.viewModel.LabViewModel
|
||||||
import org.koin.android.ext.koin.androidContext
|
import com.approagency.drug.utils.Config
|
||||||
import org.koin.core.module.dsl.viewModel
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.dsl.module
|
import org.koin.core.module.dsl.viewModel
|
||||||
import retrofit2.Retrofit
|
import org.koin.dsl.module
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
val appModule= module {
|
|
||||||
|
val appModule= module {
|
||||||
single { LabDatabase.getInstance(androidContext()) }
|
|
||||||
|
single { LabDatabase.getInstance(androidContext()) }
|
||||||
single { get<LabDatabase>().testGroupDao() }
|
|
||||||
single { get<LabDatabase>().testItemDao() }
|
single { get<LabDatabase>().testGroupDao() }
|
||||||
|
single { get<LabDatabase>().testItemDao() }
|
||||||
single {
|
|
||||||
Retrofit.Builder()
|
single {
|
||||||
.baseUrl(Config.BASE_URL)
|
Retrofit.Builder()
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.baseUrl(Config.BASE_URL)
|
||||||
.build()
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
}
|
.build()
|
||||||
single<DrugApiService> {
|
}
|
||||||
get<Retrofit>().create(DrugApiService::class.java)
|
single<DrugApiService> {
|
||||||
}
|
get<Retrofit>().create(DrugApiService::class.java)
|
||||||
|
}
|
||||||
//repo
|
|
||||||
single<DrugRepository> {
|
//repo
|
||||||
DrugRepositoryImpl(get())
|
single<DrugRepository> {
|
||||||
}
|
DrugRepositoryImpl(get())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//UseCase
|
|
||||||
single {
|
//UseCase
|
||||||
GetDrugSearchUseCase(get())
|
single {
|
||||||
|
GetDrugSearchUseCase(get())
|
||||||
}
|
|
||||||
|
}
|
||||||
single{
|
|
||||||
GetDrugDetailUseCase(get())
|
single{
|
||||||
}
|
GetDrugDetailUseCase(get())
|
||||||
|
}
|
||||||
single {
|
|
||||||
GetDarmanUseCase(get())
|
single {
|
||||||
}
|
GetDarmanUseCase(get())
|
||||||
|
}
|
||||||
single {
|
|
||||||
GetTestGroupUseCase(get())
|
single {
|
||||||
}
|
GetTestGroupUseCase(get())
|
||||||
|
}
|
||||||
single {
|
|
||||||
GetTestItemByGroupId(get())
|
single {
|
||||||
}
|
GetTestItemByGroupId(get())
|
||||||
|
}
|
||||||
single { LabRepositoryImpl(get(), get()) }
|
|
||||||
//view model
|
single { LabRepositoryImpl(get(), get()) }
|
||||||
viewModel {
|
|
||||||
HomeViewModel(get() , get() , get())
|
single { SearchTestsUseCase(get()) }
|
||||||
}
|
//view model
|
||||||
|
viewModel {
|
||||||
viewModel {
|
HomeViewModel(get() , get() , get())
|
||||||
LabViewModel(get() , get())
|
}
|
||||||
}
|
|
||||||
|
viewModel {
|
||||||
|
LabViewModel(get() , get() , get())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.approagency.drug.domain.model
|
package com.approagency.drug.domain.model
|
||||||
|
|
||||||
data class DrugSearchParams(
|
data class DrugSearchParams(
|
||||||
val query: String? = null,
|
val query: String? = null,
|
||||||
val perPage: Int = 20,
|
val perPage: Int = 20,
|
||||||
val withRelations: Boolean = true,
|
val withRelations: Boolean = true,
|
||||||
val drugGroup: Int? = null,
|
val drugGroup: Int? = null,
|
||||||
val healGroup: Int? = null,
|
val healGroup: Int? = null,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
package com.approagency.drug.domain.model
|
package com.approagency.drug.domain.model
|
||||||
|
|
||||||
data class TestGroup(
|
import com.approagency.drug.data.local.entities.TestGroupEntity
|
||||||
val id: Int,
|
|
||||||
val ename: String?,
|
data class TestGroup(
|
||||||
val fname: String?,
|
val id: Int,
|
||||||
val detail: String?,
|
val ename: String?,
|
||||||
val isParent: Boolean
|
val fname: String?,
|
||||||
)
|
val detail: String?,
|
||||||
|
val isParent: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
fun TestGroupEntity.toTestGroup(): TestGroup {
|
||||||
|
return TestGroup(
|
||||||
|
id = this.id,
|
||||||
|
ename = this.ename,
|
||||||
|
fname = this.fname,
|
||||||
|
detail = this.detail,
|
||||||
|
isParent = this.isParent
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,9 +1,20 @@
|
|||||||
package com.approagency.drug.domain.model
|
package com.approagency.drug.domain.model
|
||||||
|
|
||||||
data class TestItem(
|
import com.approagency.drug.data.local.entities.TestItemEntity
|
||||||
val id: Int,
|
|
||||||
val groupId: Int,
|
data class TestItem(
|
||||||
val title: String?,
|
val id: Int,
|
||||||
val normalValue: String?,
|
val groupId: Int,
|
||||||
val detail: String?
|
val title: String?,
|
||||||
)
|
val normalValue: String?,
|
||||||
|
val detail: String?
|
||||||
|
)
|
||||||
|
fun TestItemEntity.toTestItem(): TestItem {
|
||||||
|
return TestItem(
|
||||||
|
id = this.id,
|
||||||
|
groupId = this.groupId,
|
||||||
|
title = this.title,
|
||||||
|
normalValue = this.normalValue,
|
||||||
|
detail = this.detail
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.approagency.drug.domain.repository
|
package com.approagency.drug.domain.repository
|
||||||
|
|
||||||
import com.approagency.drug.data.dto.DarmanModel
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.data.dto.DrugModels
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import com.approagency.drug.domain.model.DrugSearchParams
|
import com.approagency.drug.domain.model.DrugSearchParams
|
||||||
|
|
||||||
interface DrugRepository {
|
interface DrugRepository {
|
||||||
suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse>
|
suspend fun searchDrug(params: DrugSearchParams): Result<DrugListResponse>
|
||||||
suspend fun drugDetail(cod:Int): Result<DrugModels>
|
suspend fun drugDetail(cod:Int): Result<DrugModels>
|
||||||
suspend fun getGorohDaroei(): Result<DarmanModel>
|
suspend fun getGorohDaroei(): Result<DarmanModel>
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.approagency.drug.domain.usecase
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
import com.approagency.drug.data.dto.DarmanModel
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
import com.approagency.drug.domain.repository.DrugRepository
|
import com.approagency.drug.domain.repository.DrugRepository
|
||||||
|
|
||||||
class GetDarmanUseCase (
|
class GetDarmanUseCase (
|
||||||
private val repository: DrugRepository
|
private val repository: DrugRepository
|
||||||
){
|
){
|
||||||
suspend operator fun invoke (): Result<DarmanModel>{
|
suspend operator fun invoke (): Result<DarmanModel>{
|
||||||
return repository.getGorohDaroei()
|
return repository.getGorohDaroei()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.approagency.drug.domain.usecase
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
import com.approagency.drug.data.dto.DrugModels
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import com.approagency.drug.domain.repository.DrugRepository
|
import com.approagency.drug.domain.repository.DrugRepository
|
||||||
|
|
||||||
class GetDrugDetailUseCase
|
class GetDrugDetailUseCase
|
||||||
(
|
(
|
||||||
private val repository: DrugRepository){
|
private val repository: DrugRepository){
|
||||||
suspend operator fun invoke(cod:Int): Result<DrugModels>{
|
suspend operator fun invoke(cod:Int): Result<DrugModels>{
|
||||||
return repository.drugDetail(cod)
|
return repository.drugDetail(cod)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.approagency.drug.domain.usecase
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.domain.model.DrugSearchParams
|
import com.approagency.drug.domain.model.DrugSearchParams
|
||||||
import com.approagency.drug.domain.repository.DrugRepository
|
import com.approagency.drug.domain.repository.DrugRepository
|
||||||
|
|
||||||
class GetDrugSearchUseCase (
|
class GetDrugSearchUseCase (
|
||||||
private val repository: DrugRepository
|
private val repository: DrugRepository
|
||||||
){
|
){
|
||||||
suspend operator fun invoke(params: DrugSearchParams): Result<DrugListResponse> {
|
suspend operator fun invoke(params: DrugSearchParams): Result<DrugListResponse> {
|
||||||
return repository.searchDrug(params)
|
return repository.searchDrug(params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
package com.approagency.drug.domain.usecase
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
import com.approagency.drug.data.repository.LabRepositoryImpl
|
import com.approagency.drug.data.repository.LabRepositoryImpl
|
||||||
import com.approagency.drug.domain.model.TestGroup
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
class GetTestGroupUseCase (
|
class GetTestGroupUseCase (
|
||||||
private val repository: LabRepositoryImpl
|
private val repository: LabRepositoryImpl
|
||||||
) {
|
) {
|
||||||
operator fun invoke(): Flow<List<TestGroup>> {
|
operator fun invoke(): Flow<List<TestGroup>> {
|
||||||
return repository.getAllGroups().map { entities ->
|
return repository.getAllGroups().map { entities ->
|
||||||
entities.map { entity ->
|
entities.map { entity ->
|
||||||
TestGroup(
|
TestGroup(
|
||||||
id = entity.id,
|
id = entity.id,
|
||||||
ename = entity.ename,
|
ename = entity.ename,
|
||||||
fname = entity.fname,
|
fname = entity.fname,
|
||||||
detail = entity.detail,
|
detail = entity.detail,
|
||||||
isParent = entity.isParent == "1"
|
isParent = entity.isParent
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,26 +1,26 @@
|
|||||||
package com.approagency.drug.domain.usecase
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
import com.approagency.drug.data.local.entities.TestItemEntity
|
import com.approagency.drug.data.local.entities.TestItemEntity
|
||||||
import com.approagency.drug.data.repository.LabRepositoryImpl
|
import com.approagency.drug.data.repository.LabRepositoryImpl
|
||||||
import com.approagency.drug.domain.model.TestItem
|
import com.approagency.drug.domain.model.TestItem
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
class GetTestItemByGroupId (
|
class GetTestItemByGroupId (
|
||||||
private val repository: LabRepositoryImpl
|
private val repository: LabRepositoryImpl
|
||||||
) {
|
) {
|
||||||
operator fun invoke(id:Int): Flow<List<TestItem>> {
|
operator fun invoke(id:Int): Flow<List<TestItem>> {
|
||||||
return repository.getItemsByGroupId(groupId = id).map {
|
return repository.getItemsByGroupId(groupId = id).map {
|
||||||
entities -> entities.map {
|
entities -> entities.map {
|
||||||
entity ->
|
entity ->
|
||||||
TestItem(
|
TestItem(
|
||||||
id = entity.id,
|
id = entity.id,
|
||||||
groupId = entity.groupId,
|
groupId = entity.groupId,
|
||||||
title = entity.title,
|
title = entity.title,
|
||||||
normalValue = entity.normalValue,
|
normalValue = entity.normalValue,
|
||||||
detail = entity.detail,
|
detail = entity.detail,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.approagency.drug.domain.usecase
|
||||||
|
|
||||||
|
|
||||||
|
import com.approagency.drug.data.repository.LabRepositoryImpl
|
||||||
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
|
import com.approagency.drug.domain.model.TestItem
|
||||||
|
import com.approagency.drug.domain.model.toTestGroup
|
||||||
|
import com.approagency.drug.domain.model.toTestItem
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
class SearchTestsUseCase(
|
||||||
|
private val repository: LabRepositoryImpl
|
||||||
|
) {
|
||||||
|
operator fun invoke(query: String): Flow<Pair<List<TestGroup>, List<TestItem>>> {
|
||||||
|
return repository.searchGroupsAndItems(query).map { (groups, items) ->
|
||||||
|
Pair(
|
||||||
|
groups.map { it.toTestGroup() },
|
||||||
|
items.map { it.toTestItem() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,24 @@
|
|||||||
package com.approagency.drug.navigation
|
package com.approagency.drug.navigation
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import com.approagency.drug.presentation.screens.HomeScreen
|
import com.approagency.drug.presentation.screens.HomeScreen
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import com.approagency.drug.presentation.screens.RootScreen
|
import com.approagency.drug.presentation.screens.RootScreen
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppNavGraph(navController: NavHostController, modifier: Modifier = Modifier) {
|
fun AppNavGraph(navController: NavHostController, modifier: Modifier = Modifier) {
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navController ,
|
navController = navController ,
|
||||||
startDestination = NavRoutes.Home.route
|
startDestination = NavRoutes.Home.route
|
||||||
){
|
){
|
||||||
composable(NavRoutes.Home.route) {
|
composable(NavRoutes.Home.route) {
|
||||||
RootScreen(
|
RootScreen(
|
||||||
navHostController = navController , modifier = Modifier
|
navHostController = navController , modifier = Modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
package com.approagency.drug.navigation
|
package com.approagency.drug.navigation
|
||||||
|
|
||||||
abstract class NavRoutes (val route:String) {
|
abstract class NavRoutes (val route:String) {
|
||||||
object Home: NavRoutes("home")
|
object Home: NavRoutes("home")
|
||||||
object Detail: NavRoutes("detail/{cod}"){
|
object Detail: NavRoutes("detail/{cod}"){
|
||||||
fun createRoute(cod: Int) = "detail/$cod"
|
fun createRoute(cod: Int) = "detail/$cod"
|
||||||
}
|
}
|
||||||
object Lab: NavRoutes("lab")
|
object Lab: NavRoutes("lab")
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,39 @@
|
|||||||
package com.approagency.drug.presentation.common
|
package com.approagency.drug.presentation.common
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.dime
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CustomModalBottomSheet(
|
fun CustomModalBottomSheet(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true),
|
||||||
scope: CoroutineScope = rememberCoroutineScope(),
|
scope: CoroutineScope = rememberCoroutineScope(),
|
||||||
onDismiss: (() -> Unit)? = null,
|
onDismiss: (() -> Unit)? = null,
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
ModalBottomSheet(
|
ModalBottomSheet(
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
onDismiss?.invoke()
|
onDismiss?.invoke()
|
||||||
},
|
},
|
||||||
sheetState = sheetState,
|
sheetState = sheetState,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
shape = RoundedCornerShape(topStart = MaterialTheme.dime.lg, topEnd = MaterialTheme.dime.lg)
|
shape = RoundedCornerShape(topStart = MaterialTheme.dime.lg, topEnd = MaterialTheme.dime.lg)
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
package com.approagency.drug.presentation.common
|
package com.approagency.drug.presentation.common
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
||||||
fun CustomBox(
|
fun CustomBox(
|
||||||
modifier: Modifier? = Modifier,
|
modifier: Modifier? = Modifier,
|
||||||
child: @Composable ()-> Unit
|
child: @Composable ()-> Unit
|
||||||
) {
|
) {
|
||||||
if (modifier != null) {
|
if (modifier != null) {
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier.clip(shape = MaterialTheme.shapes.large).background(
|
modifier = modifier.clip(shape = MaterialTheme.shapes.large).background(
|
||||||
color = MaterialTheme.colorScheme.surfaceContainer
|
color = MaterialTheme.colorScheme.surfaceContainer
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
child()
|
child()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,40 +1,40 @@
|
|||||||
package com.approagency.drug.presentation.common
|
package com.approagency.drug.presentation.common
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.ColumnScope
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CustomModalDialog(
|
fun CustomModalDialog(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
content: @Composable ColumnScope.() -> Unit
|
content: @Composable ColumnScope.() -> Unit
|
||||||
) {
|
) {
|
||||||
Dialog(
|
Dialog(
|
||||||
onDismissRequest = onDismissRequest
|
onDismissRequest = onDismissRequest
|
||||||
){
|
){
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.wrapContentHeight(),
|
.wrapContentHeight(),
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
color = MaterialTheme.colorScheme.surface,
|
color = MaterialTheme.colorScheme.surface,
|
||||||
tonalElevation = MaterialTheme.dime.sm
|
tonalElevation = MaterialTheme.dime.sm
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(MaterialTheme.dime.lg),
|
.padding(MaterialTheme.dime.lg),
|
||||||
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.lg),
|
verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.lg),
|
||||||
content = content
|
content = content
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
package com.approagency.drug.presentation.common
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.material.icons.filled.Search
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
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.onFocusChanged
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
import androidx.compose.ui.text.input.VisualTransformation
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CustomTextFilled(
|
||||||
|
value: String = "",
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
onSearch: (String) -> Unit = {},
|
||||||
|
placeholder: String = "جستجو...",
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
readOnly: Boolean = false,
|
||||||
|
singleLine: Boolean = true,
|
||||||
|
showClearButton: Boolean = true,
|
||||||
|
showSearchButton: Boolean = true,
|
||||||
|
autoSearch: Boolean = false,
|
||||||
|
searchDelay: Long = 500L,
|
||||||
|
keyboardOptions: KeyboardOptions = KeyboardOptions(
|
||||||
|
imeAction = ImeAction.Search
|
||||||
|
),
|
||||||
|
keyboardActions: KeyboardActions = KeyboardActions(
|
||||||
|
onSearch = {
|
||||||
|
if (value.isNotBlank()) {
|
||||||
|
onSearch(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
textStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.bodyLarge,
|
||||||
|
hintStyle: androidx.compose.ui.text.TextStyle = MaterialTheme.typography.bodyMedium.copy(
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
),
|
||||||
|
containerColor: androidx.compose.ui.graphics.Color = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
borderColor: androidx.compose.ui.graphics.Color = MaterialTheme.colorScheme.primaryContainer,
|
||||||
|
shape: androidx.compose.ui.graphics.Shape = MaterialTheme.shapes.medium,
|
||||||
|
height: Int = 45,
|
||||||
|
onFocusChange: ((Boolean) -> Unit)? = null
|
||||||
|
) {
|
||||||
|
var isFocused by remember { mutableStateOf(false) }
|
||||||
|
var localValue by remember(value) { mutableStateOf(value) }
|
||||||
|
|
||||||
|
// Auto-search functionality
|
||||||
|
androidx.compose.runtime.LaunchedEffect(autoSearch, searchDelay, localValue) {
|
||||||
|
if (autoSearch && localValue.isNotBlank()) {
|
||||||
|
kotlinx.coroutines.delay(searchDelay)
|
||||||
|
onSearch(localValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(height.dp)
|
||||||
|
.background(
|
||||||
|
color = containerColor,
|
||||||
|
shape = shape
|
||||||
|
)
|
||||||
|
.border(
|
||||||
|
width = 1.dp,
|
||||||
|
color = if (isFocused)
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
else
|
||||||
|
borderColor,
|
||||||
|
shape = shape
|
||||||
|
)
|
||||||
|
.padding(horizontal = MaterialTheme.dime.md, vertical = MaterialTheme.dime.sm)
|
||||||
|
.onFocusChanged { focusState ->
|
||||||
|
isFocused = focusState.isFocused
|
||||||
|
onFocusChange?.invoke(focusState.isFocused)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
// Clear button (shows when there's text and clear button is enabled)
|
||||||
|
if (showClearButton && localValue.isNotEmpty()) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
localValue = ""
|
||||||
|
onValueChange("")
|
||||||
|
},
|
||||||
|
modifier = Modifier.size(28.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Close,
|
||||||
|
contentDescription = "Clear",
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Text field
|
||||||
|
BasicTextField(
|
||||||
|
value = localValue,
|
||||||
|
onValueChange = { newValue ->
|
||||||
|
localValue = newValue
|
||||||
|
onValueChange(newValue)
|
||||||
|
},
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(horizontal = if (showClearButton && localValue.isNotEmpty()) 4.dp else 0.dp),
|
||||||
|
enabled = enabled,
|
||||||
|
readOnly = readOnly,
|
||||||
|
singleLine = singleLine,
|
||||||
|
textStyle = textStyle,
|
||||||
|
keyboardOptions = keyboardOptions,
|
||||||
|
keyboardActions = keyboardActions,
|
||||||
|
decorationBox = { innerTextField ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
contentAlignment = Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
if (localValue.isEmpty() && !isFocused) {
|
||||||
|
Text(
|
||||||
|
text = placeholder,
|
||||||
|
style = hintStyle
|
||||||
|
)
|
||||||
|
}
|
||||||
|
innerTextField()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Search button
|
||||||
|
if (showSearchButton) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
if (localValue.isNotBlank()) {
|
||||||
|
onSearch(localValue)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.size(32.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Search,
|
||||||
|
contentDescription = "Search",
|
||||||
|
modifier = Modifier.size(18.dp),
|
||||||
|
tint = if (localValue.isNotBlank())
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
else
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
package com.approagency.drug.presentation.common
|
package com.approagency.drug.presentation.common
|
||||||
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
object SnackbarManager {
|
object SnackbarManager {
|
||||||
private var snackbarHostState: SnackbarHostState? = null
|
private var snackbarHostState: SnackbarHostState? = null
|
||||||
private var coroutineScope: CoroutineScope? = null
|
private var coroutineScope: CoroutineScope? = null
|
||||||
|
|
||||||
fun init(scope: CoroutineScope, hostState: SnackbarHostState) {
|
fun init(scope: CoroutineScope, hostState: SnackbarHostState) {
|
||||||
coroutineScope = scope
|
coroutineScope = scope
|
||||||
snackbarHostState = hostState
|
snackbarHostState = hostState
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showMessage(message: String) {
|
fun showMessage(message: String) {
|
||||||
coroutineScope?.launch {
|
coroutineScope?.launch {
|
||||||
snackbarHostState?.showSnackbar(message)
|
snackbarHostState?.showSnackbar(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.vada.caller.presentation.common
|
package com.vada.caller.presentation.common
|
||||||
|
|
||||||
sealed interface UiState<out T> {
|
sealed interface UiState<out T> {
|
||||||
object Loading : UiState<Nothing>
|
object Loading : UiState<Nothing>
|
||||||
data class Success<T>(val data: T) : UiState<T>
|
data class Success<T>(val data: T) : UiState<T>
|
||||||
data class Error(val message: String) : UiState<Nothing>
|
data class Error(val message: String) : UiState<Nothing>
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,25 @@
|
|||||||
package com.approagency.drug.presentation.common
|
package com.approagency.drug.presentation.common
|
||||||
|
|
||||||
import android.content.res.Resources.Theme
|
import android.content.res.Resources.Theme
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.wrapContentHeight
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Loading(
|
fun Loading(
|
||||||
color: Color = MaterialTheme.colorScheme.onPrimary,
|
color: Color = MaterialTheme.colorScheme.onPrimary,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
color = color,
|
color = color,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
strokeWidth = 2.dp
|
strokeWidth = 2.dp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,39 @@
|
|||||||
package com.approagency.drug.presentation.common
|
package com.approagency.drug.presentation.common
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PrimaryButton(
|
fun PrimaryButton(
|
||||||
text: String,
|
text: String,
|
||||||
isLoading: Boolean?,
|
isLoading: Boolean?,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
height: Int? = null
|
height: Int? = null
|
||||||
) {
|
) {
|
||||||
Button(
|
Button(
|
||||||
colors = ButtonDefaults.buttonColors(
|
colors = ButtonDefaults.buttonColors(
|
||||||
containerColor = MaterialTheme.colorScheme.primary,
|
containerColor = MaterialTheme.colorScheme.primary,
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimary
|
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||||
),
|
),
|
||||||
shape = MaterialTheme.shapes.medium,
|
shape = MaterialTheme.shapes.medium,
|
||||||
modifier = Modifier.fillMaxWidth().height(height?.dp ?: 50.dp),
|
modifier = Modifier.fillMaxWidth().height(height?.dp ?: 50.dp),
|
||||||
onClick = {
|
onClick = {
|
||||||
onClick()
|
onClick()
|
||||||
},
|
},
|
||||||
) {
|
) {
|
||||||
if (isLoading == true) {
|
if (isLoading == true) {
|
||||||
Text("... در حال جستجو" , textAlign = TextAlign.Right)
|
Text("... در حال جستجو" , textAlign = TextAlign.Right)
|
||||||
} else {
|
} else {
|
||||||
Text(text)
|
Text(text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,156 +1,131 @@
|
|||||||
package com.approagency.drug.presentation.components
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.AbsoluteAlignment
|
import androidx.compose.ui.AbsoluteAlignment
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.approagency.drug.data.dto.DarmanList
|
import com.approagency.drug.data.dto.DarmanList
|
||||||
import com.approagency.drug.data.dto.DarmanModel
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
import com.approagency.drug.data.dto.DrugDetail
|
import com.approagency.drug.data.dto.DrugDetail
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.presentation.common.CustomBox
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
import com.approagency.drug.presentation.common.PrimaryButton
|
import com.approagency.drug.presentation.common.PrimaryButton
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.dime
|
||||||
import kotlin.collections.emptyList
|
import kotlin.collections.emptyList
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DarmanContent(
|
fun DarmanContent(
|
||||||
darmanData: Result<DarmanModel?>?,
|
darmanData: Result<DarmanModel?>?,
|
||||||
onDarmanClick: (Int) -> Unit,
|
onDarmanClick: (Int) -> Unit,
|
||||||
onRetryClick:()-> Unit,
|
onRetryClick:()-> Unit,
|
||||||
){
|
){
|
||||||
val drugs = darmanData?.getOrNull()?.data ?: emptyList()
|
val drugs = darmanData?.getOrNull()?.data ?: emptyList()
|
||||||
if (drugs.isEmpty()){
|
if (drugs.isEmpty()){
|
||||||
CustomBox(
|
RetryContent(
|
||||||
child = {
|
modifier = Modifier,
|
||||||
Column {
|
error = "مشکلی از سمت سرور پیش آمده",
|
||||||
Row(
|
onClick = {
|
||||||
horizontalArrangement = Arrangement.Center,
|
onRetryClick()
|
||||||
modifier = Modifier
|
}
|
||||||
.fillMaxWidth()
|
)
|
||||||
.padding(all = MaterialTheme.dime.sm),
|
} else {
|
||||||
) {
|
LazyColumn {
|
||||||
Text(
|
items(drugs.count() , itemContent ={
|
||||||
text = "مشکل در دریافت اطلاعات",
|
i -> DarmanItem(
|
||||||
fontWeight = FontWeight.W500
|
darman = darmanData?.getOrNull()!!.data[i] ,
|
||||||
)
|
onClick = { onDarmanClick(drugs[i].cod!!) }
|
||||||
Spacer(modifier = Modifier.width(MaterialTheme.dime.xs))
|
)
|
||||||
Icon(
|
} )
|
||||||
imageVector = Icons.Default.Search,
|
}
|
||||||
contentDescription = "Search"
|
|
||||||
)
|
}
|
||||||
}
|
|
||||||
PrimaryButton(
|
|
||||||
text = "تلاش دوباره",
|
}
|
||||||
height = 45,
|
|
||||||
isLoading = false,
|
@Composable
|
||||||
onClick = {
|
fun DarmanItem(
|
||||||
onRetryClick()
|
darman: DarmanList, // Adjust this based on your actual data class
|
||||||
}
|
onClick: (Int?) -> Unit
|
||||||
)
|
) {
|
||||||
}
|
CustomBox(
|
||||||
}
|
modifier = Modifier
|
||||||
)
|
.padding(bottom = MaterialTheme.dime.sm)
|
||||||
}else {
|
.clickable(
|
||||||
LazyColumn(
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
){
|
) {
|
||||||
items(drugs.count() , itemContent ={
|
onClick(darman.cod)
|
||||||
i -> DarmanItem(
|
},
|
||||||
darman = darmanData?.getOrNull()!!.data[i] ,
|
child = {
|
||||||
onClick = { onDarmanClick(drugs[i].cod!!) }
|
Row(
|
||||||
)
|
modifier = Modifier.padding(all = MaterialTheme.dime.lg),
|
||||||
} )
|
) {
|
||||||
}
|
Column (
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
}
|
horizontalAlignment = Alignment.Start
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
}
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = AbsoluteAlignment.Right
|
||||||
@Composable
|
// horizontalArrangement = Arrangement.SpaceBetween
|
||||||
fun DarmanItem(
|
) {
|
||||||
darman: DarmanList, // Adjust this based on your actual data class
|
Text(darman.nam_fa.toString() , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right )
|
||||||
onClick: (Int?) -> Unit
|
Text(darman.nam_en.toString() , style = MaterialTheme.typography.bodySmall)
|
||||||
) {
|
}
|
||||||
CustomBox(
|
Row (
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth(),
|
||||||
.padding(bottom = MaterialTheme.dime.sm)
|
horizontalArrangement = Arrangement.Center
|
||||||
.clickable(
|
) {
|
||||||
indication = null,
|
TextButton(
|
||||||
interactionSource = remember { MutableInteractionSource() }
|
onClick = {
|
||||||
) {
|
onClick(darman.cod)
|
||||||
onClick(darman.cod)
|
}
|
||||||
},
|
) {
|
||||||
child = {
|
Row(
|
||||||
Row(
|
horizontalArrangement = Arrangement.Center
|
||||||
modifier = Modifier.padding(all = MaterialTheme.dime.lg),
|
) {
|
||||||
) {
|
Icon(
|
||||||
Column (
|
imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
|
||||||
verticalArrangement = Arrangement.Center,
|
, contentDescription = "see more"
|
||||||
horizontalAlignment = Alignment.Start
|
)
|
||||||
) {
|
Text("مشاهده لیست")
|
||||||
Column(
|
}
|
||||||
modifier = Modifier.fillMaxWidth(),
|
}
|
||||||
horizontalAlignment = AbsoluteAlignment.Right
|
}
|
||||||
// horizontalArrangement = Arrangement.SpaceBetween
|
}
|
||||||
) {
|
|
||||||
Text(darman.nam_fa.toString() , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right )
|
}
|
||||||
Text(darman.nam_en.toString() , style = MaterialTheme.typography.bodySmall)
|
}
|
||||||
}
|
)
|
||||||
Row (
|
// Button(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
// onClick = onClick,
|
||||||
horizontalArrangement = Arrangement.Center
|
// modifier = Modifier
|
||||||
) {
|
// .fillMaxWidth()
|
||||||
TextButton(
|
// .padding(vertical = 4.dp)
|
||||||
onClick = {
|
// ) {
|
||||||
onClick(darman.cod)
|
// drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model
|
||||||
}
|
// }
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
horizontalArrangement = Arrangement.Center
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
|
|
||||||
, contentDescription = "see more"
|
|
||||||
)
|
|
||||||
Text("مشاهده لیست")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// Button(
|
|
||||||
// onClick = onClick,
|
|
||||||
// modifier = Modifier
|
|
||||||
// .fillMaxWidth()
|
|
||||||
// .padding(vertical = 4.dp)
|
|
||||||
// ) {
|
|
||||||
// drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
+151
-151
@@ -1,152 +1,152 @@
|
|||||||
package com.approagency.drug.presentation.components
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import com.approagency.drug.data.dto.DrugDetail
|
import com.approagency.drug.data.dto.DrugDetail
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.presentation.common.CustomBox
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DrugListContent(
|
fun DrugListContent(
|
||||||
drugsData: Result<DrugListResponse?>?,
|
drugsData: Result<DrugListResponse?>?,
|
||||||
onDrugClick: (Int) -> Unit
|
onDrugClick: (Int) -> Unit
|
||||||
) {
|
) {
|
||||||
val drugs = drugsData?.getOrNull()?.data ?: emptyList()
|
val drugs = drugsData?.getOrNull()?.data ?: emptyList()
|
||||||
|
|
||||||
if (drugs.isEmpty()) {
|
if (drugs.isEmpty()) {
|
||||||
CustomBox(
|
CustomBox(
|
||||||
child = {
|
child = {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.Center,
|
horizontalArrangement = Arrangement.Center,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(all = MaterialTheme.dime.sm),
|
.padding(all = MaterialTheme.dime.sm),
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = "نتیجهای یافت نشد",
|
text = "نتیجهای یافت نشد",
|
||||||
fontWeight = FontWeight.W500
|
fontWeight = FontWeight.W500
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(MaterialTheme.dime.xs))
|
Spacer(modifier = Modifier.width(MaterialTheme.dime.xs))
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Search,
|
imageVector = Icons.Default.Search,
|
||||||
contentDescription = "Search"
|
contentDescription = "Search"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
|
||||||
){
|
){
|
||||||
items(drugs.count() , itemContent ={
|
items(drugs.count() , itemContent ={
|
||||||
i -> DrugItem(
|
i -> DrugItem(
|
||||||
drug = drugs[i],
|
drug = drugs[i],
|
||||||
onClick = { onDrugClick(drugs[i].cod!!) }
|
onClick = { onDrugClick(drugs[i].cod!!) }
|
||||||
)
|
)
|
||||||
} )
|
} )
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DrugItem(
|
fun DrugItem(
|
||||||
drug: DrugDetail, // Adjust this based on your actual data class
|
drug: DrugDetail, // Adjust this based on your actual data class
|
||||||
onClick: (Int?) -> Unit
|
onClick: (Int?) -> Unit
|
||||||
) {
|
) {
|
||||||
CustomBox(
|
CustomBox(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(bottom = MaterialTheme.dime.sm)
|
.padding(bottom = MaterialTheme.dime.sm)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable(
|
.clickable(
|
||||||
indication = null,
|
indication = null,
|
||||||
interactionSource = remember { MutableInteractionSource() }
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
) {
|
) {
|
||||||
onClick(drug.cod)
|
onClick(drug.cod)
|
||||||
},
|
},
|
||||||
child = {
|
child = {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.padding(all = MaterialTheme.dime.lg),
|
modifier = Modifier.padding(all = MaterialTheme.dime.lg),
|
||||||
) {
|
) {
|
||||||
Column (
|
Column (
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.Start
|
horizontalAlignment = Alignment.Start
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
Text(drug.nam_en.toString() , style = MaterialTheme.typography.bodySmall)
|
Text(drug.nam_en.toString() , style = MaterialTheme.typography.bodySmall)
|
||||||
Text(drug.nam_fa.toString() , style = MaterialTheme.typography.labelLarge,textAlign = TextAlign.Right)
|
Text(drug.nam_fa.toString() , style = MaterialTheme.typography.labelLarge,textAlign = TextAlign.Right)
|
||||||
}
|
}
|
||||||
if (drug.goroh_darmani != null )
|
if (drug.goroh_darmani != null )
|
||||||
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
, horizontalArrangement = Arrangement.End
|
, horizontalArrangement = Arrangement.End
|
||||||
) {Text(
|
) {Text(
|
||||||
"گروه دارویی: ${drug.goroh_darmani.nam_fa.toString()}",
|
"گروه دارویی: ${drug.goroh_darmani.nam_fa.toString()}",
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
textAlign = TextAlign.Right
|
textAlign = TextAlign.Right
|
||||||
) }
|
) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Row (
|
Row (
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
onClick(drug.cod)
|
onClick(drug.cod)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
|
imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
|
||||||
, contentDescription = "see more"
|
, contentDescription = "see more"
|
||||||
)
|
)
|
||||||
Text("مشاهده جزییات")
|
Text("مشاهده جزییات")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// Button(
|
// Button(
|
||||||
// onClick = onClick,
|
// onClick = onClick,
|
||||||
// modifier = Modifier
|
// modifier = Modifier
|
||||||
// .fillMaxWidth()
|
// .fillMaxWidth()
|
||||||
// .padding(vertical = 4.dp)
|
// .padding(vertical = 4.dp)
|
||||||
// ) {
|
// ) {
|
||||||
// drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model
|
// drug.nam_fa?.let { Text(text = it) } // Adjust based on your drug model
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
+39
-39
@@ -1,40 +1,40 @@
|
|||||||
package com.approagency.drug.presentation.components
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import com.approagency.drug.presentation.common.CustomBox
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun EmptyStateContent() {
|
fun EmptyStateContent() {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
, verticalArrangement = Arrangement.Center
|
, verticalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Search,
|
imageVector = Icons.Default.Search,
|
||||||
contentDescription = "Search",
|
contentDescription = "Search",
|
||||||
modifier = Modifier.size(MaterialTheme.dime.iconLg)
|
modifier = Modifier.size(MaterialTheme.dime.iconLg)
|
||||||
)
|
)
|
||||||
CustomBox(
|
CustomBox(
|
||||||
child = {
|
child = {
|
||||||
Text(
|
Text(
|
||||||
text = "برای جستجو، نام دارو را وارد کنید",
|
text = "برای جستجو، نام دارو را وارد کنید",
|
||||||
modifier = Modifier.padding(MaterialTheme.dime.sm)
|
modifier = Modifier.padding(MaterialTheme.dime.sm)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
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 com.approagency.drug.presentation.common.CustomBox
|
||||||
|
import com.approagency.drug.presentation.common.PrimaryButton
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun RetryContent(modifier: Modifier = Modifier , error:String ,onClick:()-> Unit ) {
|
||||||
|
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 = { onClick() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
+205
@@ -0,0 +1,205 @@
|
|||||||
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
|
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.fillMaxWidth
|
||||||
|
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.HorizontalDivider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
|
import com.approagency.drug.domain.model.TestItem
|
||||||
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
|
import com.approagency.drug.presentation.common.Loading
|
||||||
|
import com.approagency.drug.presentation.viewModel.SearchResult
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.ui.AbsoluteAlignment
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextDirection
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SearchResultsContent(
|
||||||
|
searchResult: SearchResult?,
|
||||||
|
isLoading: Boolean,
|
||||||
|
error: String?,
|
||||||
|
onGroupClick: (TestGroup) -> Unit,
|
||||||
|
onItemClick: (TestItem) -> Unit,
|
||||||
|
onClearSearch: () -> Unit
|
||||||
|
) {
|
||||||
|
when {
|
||||||
|
isLoading -> {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Loading(color = MaterialTheme.colorScheme.primary)
|
||||||
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
|
||||||
|
Text("در حال جستجو...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error != null -> {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Text(
|
||||||
|
text = "خطا در جستجو",
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = error,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
modifier = Modifier.padding(vertical = 8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
searchResult != null -> {
|
||||||
|
val groups = searchResult.groups
|
||||||
|
val items = searchResult.items
|
||||||
|
|
||||||
|
if (groups.isEmpty() && items.isEmpty()) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
|
Text(
|
||||||
|
text = "نتیجهای یافت نشد",
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
|
||||||
|
Text(
|
||||||
|
text = "لطفاً عبارت دیگری را جستجو کنید",
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(vertical = MaterialTheme.dime.md),
|
||||||
|
horizontalAlignment = AbsoluteAlignment.Right
|
||||||
|
|
||||||
|
) {
|
||||||
|
if (groups.isNotEmpty()) {
|
||||||
|
item {
|
||||||
|
Text(
|
||||||
|
text = "گروههای آزمایشگاهی (${groups.size})",
|
||||||
|
style = MaterialTheme.typography.titleMedium.copy(
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
),
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.padding(vertical = MaterialTheme.dime.md)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
items(groups) { group ->
|
||||||
|
TestGroupItemContent(
|
||||||
|
group = group,
|
||||||
|
onClick = { onGroupClick(group) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items.isNotEmpty()) {
|
||||||
|
item {
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.padding(vertical = MaterialTheme.dime.md)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "آیتمهای آزمایشگاهی (${items.size})",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.padding(vertical = MaterialTheme.dime.md)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
items(items) { item ->
|
||||||
|
TestItemSearchResult(
|
||||||
|
testItem = item,
|
||||||
|
onClick = { onItemClick(item) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TestItemSearchResult(
|
||||||
|
testItem: TestItem,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
CustomBox(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = MaterialTheme.dime.sm)
|
||||||
|
.clickable(
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) {
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(all = MaterialTheme.dime.lg),
|
||||||
|
horizontalAlignment = AbsoluteAlignment.Right
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = testItem.title ?: "بدون عنوان",
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
fontWeight = FontWeight.SemiBold
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!testItem.normalValue.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
text = "مقدار نرمال: ${testItem.normalValue}",
|
||||||
|
style = MaterialTheme.typography.bodySmall.copy(
|
||||||
|
textDirection = TextDirection.Rtl,
|
||||||
|
textAlign = TextAlign.Right
|
||||||
|
),
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.padding(top = MaterialTheme.dime.xs)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!testItem.detail.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
text = testItem.detail,
|
||||||
|
style = MaterialTheme.typography.bodySmall.copy(
|
||||||
|
textDirection = TextDirection.Rtl,
|
||||||
|
textAlign = TextAlign.Right
|
||||||
|
),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(top = MaterialTheme.dime.sm)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
+225
-225
@@ -1,225 +1,225 @@
|
|||||||
package com.approagency.drug.presentation.components
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items // Import items if you were to use it for lists within LazyColumn
|
import androidx.compose.foundation.lazy.items // Import items if you were to use it for lists within LazyColumn
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import com.approagency.drug.data.dto.DrugDetail // Import DrugDetail directly
|
import com.approagency.drug.data.dto.DrugDetail // Import DrugDetail directly
|
||||||
import com.approagency.drug.data.dto.DrugModels
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import com.approagency.drug.presentation.common.CustomModalBottomSheet
|
import com.approagency.drug.presentation.common.CustomModalBottomSheet
|
||||||
import com.approagency.drug.presentation.viewModel.DrugDetailState
|
import com.approagency.drug.presentation.viewModel.DrugDetailState
|
||||||
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
||||||
import com.vada.caller.ui.theme.dime // Assuming this is your custom spacing/dimension object
|
import com.vada.caller.ui.theme.dime // Assuming this is your custom spacing/dimension object
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
fun DrugDetailSheet(
|
fun DrugDetailSheet(
|
||||||
viewModel: HomeViewModel,
|
viewModel: HomeViewModel,
|
||||||
state: DrugDetailState // Assuming you pass the state containing the detail
|
state: DrugDetailState // Assuming you pass the state containing the detail
|
||||||
) {
|
) {
|
||||||
val detail: DrugDetail? = state.drugDetail?.getOrNull()?.data
|
val detail: DrugDetail? = state.drugDetail?.getOrNull()?.data
|
||||||
|
|
||||||
// Only show the sheet if detail is not null
|
// Only show the sheet if detail is not null
|
||||||
if (detail != null) {
|
if (detail != null) {
|
||||||
CustomModalBottomSheet(
|
CustomModalBottomSheet(
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
viewModel.closeDetail()
|
viewModel.closeDetail()
|
||||||
},
|
},
|
||||||
content = {
|
content = {
|
||||||
// Use a LazyColumn for scrollable content inside the sheet
|
// Use a LazyColumn for scrollable content inside the sheet
|
||||||
|
|
||||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = MaterialTheme.dime.lg),
|
.padding(horizontal = MaterialTheme.dime.lg),
|
||||||
contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg) // Add padding to the bottom of the content for better scrolling
|
contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg) // Add padding to the bottom of the content for better scrolling
|
||||||
) {
|
) {
|
||||||
// --- 1. Main Title ---
|
// --- 1. Main Title ---
|
||||||
item { // Use item {} for single composables within LazyColumn
|
item { // Use item {} for single composables within LazyColumn
|
||||||
Text(
|
Text(
|
||||||
text = detail.nam_fa ?: "جزئیات دارو",
|
text = detail.nam_fa ?: "جزئیات دارو",
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
modifier = Modifier.padding(bottom = MaterialTheme.dime.md)
|
modifier = Modifier.padding(bottom = MaterialTheme.dime.md)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = detail.nam_en ?: "N/A (English Name)",
|
text = detail.nam_en ?: "N/A (English Name)",
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
modifier = Modifier.padding(bottom = MaterialTheme.dime.lg)
|
modifier = Modifier.padding(bottom = MaterialTheme.dime.lg)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Divider for visual separation
|
// Divider for visual separation
|
||||||
HorizontalDivider(Modifier.padding(bottom = MaterialTheme.dime.md))
|
HorizontalDivider(Modifier.padding(bottom = MaterialTheme.dime.md))
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 2. Key Groupings ---
|
// --- 2. Key Groupings ---
|
||||||
// Goroh Darmani (Pharmacological Group)
|
// Goroh Darmani (Pharmacological Group)
|
||||||
detail.goroh_darmani?.let { group ->
|
detail.goroh_darmani?.let { group ->
|
||||||
item {
|
item {
|
||||||
DetailRow(
|
DetailRow(
|
||||||
title = "گروه درمانی",
|
title = "گروه درمانی",
|
||||||
value = group.nam_fa ?: "نامشخص"
|
value = group.nam_fa ?: "نامشخص"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
group.nam_en?.let { enName ->
|
group.nam_en?.let { enName ->
|
||||||
item {
|
item {
|
||||||
DetailRow(
|
DetailRow(
|
||||||
title = "Group (EN)",
|
title = "Group (EN)",
|
||||||
value = enName
|
value = enName
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Goroh Darmanei (Therapeutic Group)
|
// Goroh Darmanei (Therapeutic Group)
|
||||||
detail.goroh_daroei?.let { group ->
|
detail.goroh_daroei?.let { group ->
|
||||||
item {
|
item {
|
||||||
DetailRow(
|
DetailRow(
|
||||||
title = "گروه دارویی",
|
title = "گروه دارویی",
|
||||||
value = group.nam ?: "نامشخص"
|
value = group.nam ?: "نامشخص"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Goroh Farmakologic (Pharmacological Class)
|
// Goroh Farmakologic (Pharmacological Class)
|
||||||
detail.goroh_farmakologic_cod?.let { cod ->
|
detail.goroh_farmakologic_cod?.let { cod ->
|
||||||
// NOTE: You might need a separate API call or mapping here
|
// NOTE: You might need a separate API call or mapping here
|
||||||
// if cod needs to be translated to a name. For now, we show the code.
|
// if cod needs to be translated to a name. For now, we show the code.
|
||||||
item {
|
item {
|
||||||
DetailRow(
|
DetailRow(
|
||||||
title = "کلاس فارماکولوژیک",
|
title = "کلاس فارماکولوژیک",
|
||||||
value = cod.toString()
|
value = cod.toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item { // Add a divider as a separate item
|
item { // Add a divider as a separate item
|
||||||
HorizontalDivider(Modifier.padding(vertical = MaterialTheme.dime.md))
|
HorizontalDivider(Modifier.padding(vertical = MaterialTheme.dime.md))
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 3. Usage & Mechanism ---
|
// --- 3. Usage & Mechanism ---
|
||||||
detail.mavaredmasraf?.let { usage ->
|
detail.mavaredmasraf?.let { usage ->
|
||||||
item {
|
item {
|
||||||
DetailSection(title = "موارد مصرف", content = usage)
|
DetailSection(title = "موارد مصرف", content = usage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
detail.mekanismtasir?.let { mechanism ->
|
detail.mekanismtasir?.let { mechanism ->
|
||||||
item {
|
item {
|
||||||
DetailSection(title = "مکانیسم اثر", content = mechanism)
|
DetailSection(title = "مکانیسم اثر", content = mechanism)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 4. Warnings & Storage ---
|
// --- 4. Warnings & Storage ---
|
||||||
detail.hoshdar?.let { warning ->
|
detail.hoshdar?.let { warning ->
|
||||||
item {
|
item {
|
||||||
DetailSection(
|
DetailSection(
|
||||||
title = "هشدارها",
|
title = "هشدارها",
|
||||||
content = warning,
|
content = warning,
|
||||||
isWarning = true
|
isWarning = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
detail.sharayetnegahdari?.let { storage ->
|
detail.sharayetnegahdari?.let { storage ->
|
||||||
item {
|
item {
|
||||||
DetailSection(
|
DetailSection(
|
||||||
title = "شرایط نگهداری",
|
title = "شرایط نگهداری",
|
||||||
content = storage
|
content = storage
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Helper Composables for Clean Code ---
|
// --- Helper Composables for Clean Code ---
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailRow(
|
fun DetailRow(
|
||||||
title: String,
|
title: String,
|
||||||
value: String
|
value: String
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = MaterialTheme.dime.sm),
|
.padding(vertical = MaterialTheme.dime.sm),
|
||||||
horizontalArrangement = Arrangement.SpaceBetween
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
textAlign = TextAlign.Right
|
textAlign = TextAlign.Right
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = value,
|
text = value,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
textAlign = TextAlign.End,
|
textAlign = TextAlign.End,
|
||||||
// Ensure text takes up available space and doesn't push other elements.
|
// Ensure text takes up available space and doesn't push other elements.
|
||||||
// Using weight(1f, fill = false) is good for aligning to the end if there's space.
|
// Using weight(1f, fill = false) is good for aligning to the end if there's space.
|
||||||
modifier = Modifier.weight(1f, fill = false)
|
modifier = Modifier.weight(1f, fill = false)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DetailSection(
|
fun DetailSection(
|
||||||
title: String,
|
title: String,
|
||||||
content: String,
|
content: String,
|
||||||
isWarning: Boolean = false
|
isWarning: Boolean = false
|
||||||
) {
|
) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(vertical = MaterialTheme.dime.md)
|
.padding(vertical = MaterialTheme.dime.md)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
color = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
|
color = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
modifier = Modifier.padding(bottom = MaterialTheme.dime.sm)
|
modifier = Modifier.padding(bottom = MaterialTheme.dime.sm)
|
||||||
)
|
)
|
||||||
// Use a text block that respects line breaks (usually done with \n)
|
// Use a text block that respects line breaks (usually done with \n)
|
||||||
Text(
|
Text(
|
||||||
text = content,
|
text = content,
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
// Consider adding softWrap = true if content might overflow
|
// Consider adding softWrap = true if content might overflow
|
||||||
// softWrap = true
|
// softWrap = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// Add divider after each section for better visual separation
|
// Add divider after each section for better visual separation
|
||||||
HorizontalDivider(Modifier.padding(top = MaterialTheme.dime.sm))
|
HorizontalDivider(Modifier.padding(top = MaterialTheme.dime.sm))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure your DrugDetailState in ViewModel looks something like this:
|
// Make sure your DrugDetailState in ViewModel looks something like this:
|
||||||
// data class DrugDetailState(
|
// data class DrugDetailState(
|
||||||
// val isLoading: Boolean = false,
|
// val isLoading: Boolean = false,
|
||||||
// val drugDetail: DrugModels? = null, // Changed to DrugModels? to match data access
|
// val drugDetail: DrugModels? = null, // Changed to DrugModels? to match data access
|
||||||
// val isDetailVisible: Boolean = false,
|
// val isDetailVisible: Boolean = false,
|
||||||
// val error: String? = null
|
// val error: String? = null
|
||||||
// )
|
// )
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
|
import com.approagency.drug.presentation.common.CustomModalBottomSheet
|
||||||
|
import com.approagency.drug.presentation.viewModel.LabViewModel
|
||||||
|
import com.approagency.drug.presentation.viewModel.TestItemState
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun TestDetailSheet(state: TestItemState, testGroup: TestGroup, onDismiss: () -> Unit) {
|
||||||
|
if (state.testItem != null) {
|
||||||
|
val testItems by state.testItem.collectAsState(initial = emptyList())
|
||||||
|
|
||||||
|
CustomModalBottomSheet(
|
||||||
|
onDismiss = onDismiss,
|
||||||
|
content = {
|
||||||
|
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = MaterialTheme.dime.lg),
|
||||||
|
contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg)
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
Text(
|
||||||
|
text = testGroup.fname ?: "جزئیات آزمایش",
|
||||||
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
modifier = Modifier.padding(bottom = MaterialTheme.dime.md)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = testGroup.ename ?: "N/A (English Name)",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(bottom = MaterialTheme.dime.sm)
|
||||||
|
)
|
||||||
|
if (testGroup.detail != null){
|
||||||
|
Text(testGroup.detail , style = MaterialTheme.typography.bodySmall,modifier = Modifier.padding(bottom = MaterialTheme.dime.md) )
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HorizontalDivider(Modifier.padding(bottom = MaterialTheme.dime.md))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (testItems.isEmpty()) {
|
||||||
|
item {
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
items(testItems) { testItem ->
|
||||||
|
TestItemDetailCard(testItem = testItem)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TestItemDetailCard(testItem: com.approagency.drug.domain.model.TestItem) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = MaterialTheme.dime.md)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = testItem.title ?: "بدون عنوان",
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.SemiBold
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!testItem.normalValue.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
text = "مقدار نرمال: ${testItem.normalValue}",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.padding(top = MaterialTheme.dime.xs)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!testItem.detail.isNullOrBlank()) {
|
||||||
|
Text(
|
||||||
|
text = testItem.detail,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(top = MaterialTheme.dime.sm)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizontalDivider(
|
||||||
|
modifier = Modifier.padding(top = MaterialTheme.dime.md),
|
||||||
|
thickness = 0.5.dp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
package com.approagency.drug.presentation.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.AbsoluteAlignment
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextDirection
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
|
import com.vada.caller.ui.theme.dime
|
||||||
|
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun TestGroupItemContent(
|
||||||
|
group: TestGroup,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
|
||||||
|
CustomBox(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(bottom = MaterialTheme.dime.sm)
|
||||||
|
.clickable(
|
||||||
|
indication = null,
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
) {
|
||||||
|
onClick()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.padding(all = MaterialTheme.dime.lg),
|
||||||
|
) {
|
||||||
|
Column (
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.Start
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalAlignment = AbsoluteAlignment.Right
|
||||||
|
// horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Text(group.fname ?: "" , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right )
|
||||||
|
Text(group.ename ?: "" , style = MaterialTheme.typography.bodySmall , textAlign = TextAlign.Left)
|
||||||
|
Spacer(modifier = Modifier.padding(vertical = MaterialTheme.dime.xs))
|
||||||
|
Text(group.detail ?: "" , style = MaterialTheme.typography.bodySmall.copy(
|
||||||
|
textDirection = TextDirection.Rtl
|
||||||
|
) , textAlign = TextAlign.Right , maxLines = 2 , softWrap = true , overflow = TextOverflow.Ellipsis)
|
||||||
|
}
|
||||||
|
Row (
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.AutoMirrored.TwoTone.KeyboardArrowLeft
|
||||||
|
, contentDescription = "see more"
|
||||||
|
)
|
||||||
|
Text("مشاهده جزییات")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,292 +1,251 @@
|
|||||||
package com.approagency.drug.presentation.screens
|
package com.approagency.drug.presentation.screens
|
||||||
|
|
||||||
import android.R
|
import android.R
|
||||||
import android.R.attr.fontWeight
|
import android.R.attr.fontWeight
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.snapping.SnapPosition
|
import androidx.compose.foundation.gestures.snapping.SnapPosition
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
||||||
import androidx.compose.material.icons.filled.Info
|
import androidx.compose.material.icons.filled.Info
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowLeft
|
import androidx.compose.material.icons.filled.KeyboardArrowLeft
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
import androidx.compose.material.icons.filled.KeyboardArrowRight
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.twotone.Info
|
import androidx.compose.material.icons.twotone.Info
|
||||||
import androidx.compose.material.icons.twotone.KeyboardArrowLeft
|
import androidx.compose.material.icons.twotone.KeyboardArrowLeft
|
||||||
import androidx.compose.material.icons.twotone.KeyboardArrowRight
|
import androidx.compose.material.icons.twotone.KeyboardArrowRight
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TextField
|
import androidx.compose.material3.TextField
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.TextUnit
|
import androidx.compose.ui.unit.TextUnit
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.approagency.drug.data.dto.DrugDetail
|
import com.approagency.drug.data.dto.DrugDetail
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.domain.model.DrugSearchParams
|
import com.approagency.drug.domain.model.DrugSearchParams
|
||||||
import com.approagency.drug.presentation.common.CustomBox
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
import com.approagency.drug.presentation.common.CustomModalBottomSheet
|
import com.approagency.drug.presentation.common.CustomModalBottomSheet
|
||||||
import com.approagency.drug.presentation.common.CustomModalDialog
|
import com.approagency.drug.presentation.common.CustomModalDialog
|
||||||
import com.approagency.drug.presentation.common.Loading
|
import com.approagency.drug.presentation.common.CustomTextFilled
|
||||||
import com.approagency.drug.presentation.common.PrimaryButton
|
import com.approagency.drug.presentation.common.Loading
|
||||||
import com.approagency.drug.presentation.components.DarmanContent
|
import com.approagency.drug.presentation.common.PrimaryButton
|
||||||
import com.approagency.drug.presentation.components.DrugDetailSheet
|
import com.approagency.drug.presentation.components.DarmanContent
|
||||||
import com.approagency.drug.presentation.components.DrugListContent
|
import com.approagency.drug.presentation.components.DrugDetailSheet
|
||||||
import com.approagency.drug.presentation.components.EmptyStateContent
|
import com.approagency.drug.presentation.components.DrugListContent
|
||||||
import com.approagency.drug.presentation.viewModel.CombineState
|
import com.approagency.drug.presentation.components.EmptyStateContent
|
||||||
import com.approagency.drug.presentation.viewModel.HomeEvent
|
import com.approagency.drug.presentation.viewModel.CombineState
|
||||||
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
import com.approagency.drug.presentation.viewModel.HomeEvent
|
||||||
import com.vada.caller.ui.theme.Dime
|
import com.approagency.drug.presentation.viewModel.HomeViewModel
|
||||||
import com.vada.caller.ui.theme.dime
|
import com.vada.caller.ui.theme.Dime
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import com.vada.caller.ui.theme.dime
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
fun HomeScreen(
|
@Composable
|
||||||
navController : NavController,
|
fun HomeScreen(
|
||||||
modifier: Modifier,
|
navController : NavController,
|
||||||
viewModel: HomeViewModel = koinViewModel()
|
modifier: Modifier,
|
||||||
) {
|
viewModel: HomeViewModel = koinViewModel()
|
||||||
val state by viewModel.uiState.collectAsState()
|
) {
|
||||||
val focusManager = LocalFocusManager.current
|
val state by viewModel.uiState.collectAsState()
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val focusManager = LocalFocusManager.current
|
||||||
if (state.drugDetailState.isDetailVisible) {
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
DrugDetailSheet(
|
if (state.drugDetailState.isDetailVisible) {
|
||||||
viewModel = viewModel,
|
DrugDetailSheet(
|
||||||
state = state.drugDetailState
|
viewModel = viewModel,
|
||||||
)
|
state = state.drugDetailState
|
||||||
}
|
)
|
||||||
HomeContent(
|
}
|
||||||
modifier = modifier,
|
HomeContent(
|
||||||
state = state,
|
modifier = modifier,
|
||||||
viewModelEvent = viewModel.event,
|
state = state,
|
||||||
onSearch = { value ->
|
viewModelEvent = viewModel.event,
|
||||||
if (value is Int) {
|
onSearch = { value ->
|
||||||
viewModel.searchDrugs(DrugSearchParams(healGroup = value))
|
if (value is Int) {
|
||||||
} else if (value is String) {
|
viewModel.searchDrugs(DrugSearchParams(healGroup = value))
|
||||||
viewModel.searchDrugs(DrugSearchParams(query = value))
|
} else if (value is String) {
|
||||||
}
|
viewModel.searchDrugs(DrugSearchParams(query = value))
|
||||||
keyboardController?.hide()
|
}
|
||||||
focusManager.clearFocus()},
|
keyboardController?.hide()
|
||||||
onRetry = {
|
focusManager.clearFocus()},
|
||||||
viewModel.getDarmani()
|
onRetry = {
|
||||||
},
|
viewModel.getDarmani()
|
||||||
onDrugClick = { drugId -> viewModel.drugDetail(drugId , allDrugs = state.drugSearchState.drugsData) }
|
},
|
||||||
)
|
onDrugClick = { drugId -> viewModel.drugDetail(drugId , allDrugs = state.drugSearchState.drugsData) }
|
||||||
}
|
)
|
||||||
|
}
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
fun HomeContent(
|
@Composable
|
||||||
state: CombineState,
|
fun HomeContent(
|
||||||
viewModelEvent: SharedFlow<HomeEvent>,
|
state: CombineState,
|
||||||
onSearch: (Any) -> Unit,
|
viewModelEvent: SharedFlow<HomeEvent>,
|
||||||
onRetry: () -> Unit,
|
onSearch: (Any) -> Unit,
|
||||||
onDrugClick: (Int) -> Unit,
|
onRetry: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
onDrugClick: (Int) -> Unit,
|
||||||
) {
|
modifier: Modifier = Modifier
|
||||||
var search by remember { mutableStateOf("") }
|
) {
|
||||||
Column(
|
var searchText by remember { mutableStateOf("") }
|
||||||
modifier = modifier
|
Column(
|
||||||
.fillMaxSize().padding(horizontal = MaterialTheme.dime.md)
|
modifier = modifier
|
||||||
.padding(top = MaterialTheme.dime.md),
|
.fillMaxSize().padding(horizontal = MaterialTheme.dime.md)
|
||||||
horizontalAlignment = Alignment.Start,
|
.padding(top = MaterialTheme.dime.md),
|
||||||
) {
|
horizontalAlignment = Alignment.Start,
|
||||||
Column (
|
) {
|
||||||
verticalArrangement = Arrangement.Center
|
Column (
|
||||||
){
|
verticalArrangement = Arrangement.Center
|
||||||
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) {
|
){
|
||||||
Box(
|
CustomTextFilled(
|
||||||
modifier = Modifier
|
value = searchText,
|
||||||
.fillMaxWidth()
|
onValueChange = { searchText = it },
|
||||||
.height(45.dp) // Your desired height
|
onSearch = { query ->
|
||||||
.background(
|
if (query.isNotBlank()) {
|
||||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
onSearch(query)
|
||||||
shape =MaterialTheme.shapes.medium
|
}
|
||||||
)
|
},
|
||||||
.border(
|
placeholder = "جستجوی دارو",
|
||||||
width = 1.dp,
|
showClearButton = true,
|
||||||
color = MaterialTheme.colorScheme.primaryContainer,
|
showSearchButton = true,
|
||||||
shape =MaterialTheme.shapes.medium
|
autoSearch = false, // Set to true if you want search while typing
|
||||||
)
|
height = 45
|
||||||
.padding(horizontal = MaterialTheme.dime.md, vertical = MaterialTheme.dime.sm)
|
)
|
||||||
) {
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
|
||||||
Row(
|
PrimaryButton(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
text = "جستجو",
|
||||||
modifier = Modifier.fillMaxSize()
|
height = 40,
|
||||||
) {
|
isLoading = state.drugSearchState.isLoading,
|
||||||
BasicTextField(
|
onClick = {
|
||||||
value = search,
|
if (searchText.isNotBlank()) {
|
||||||
onValueChange = { search = it },
|
onSearch(searchText)
|
||||||
modifier = Modifier.weight(1f),
|
}
|
||||||
singleLine = true,
|
}
|
||||||
textStyle = MaterialTheme.typography.bodyLarge,
|
)
|
||||||
decorationBox = { innerTextField ->
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
|
||||||
Box {
|
Box(modifier = Modifier.fillMaxSize()) {
|
||||||
if (search.isEmpty()) {
|
|
||||||
Text(
|
// Always draw the data if available
|
||||||
"جستجوی دارو",
|
if (state.drugSearchState.drugsData != null && !state.showDarmanList) {
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
Column {
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
Row(
|
||||||
)
|
horizontalArrangement = Arrangement.End,
|
||||||
}
|
modifier = Modifier.fillMaxWidth()
|
||||||
innerTextField()
|
) {
|
||||||
}
|
TextButton(
|
||||||
}
|
onClick = {
|
||||||
)
|
onRetry()
|
||||||
IconButton(
|
}
|
||||||
onClick = {
|
) {
|
||||||
if (search.isNotBlank()) {
|
Text("مشاهده تمامی گروه های دارویی")
|
||||||
onSearch(search)
|
Icon(
|
||||||
}
|
imageVector = Icons.Default.KeyboardArrowRight,
|
||||||
},
|
contentDescription = "back"
|
||||||
modifier = Modifier.size(32.dp)
|
)
|
||||||
) {
|
Spacer(modifier = Modifier.width(MaterialTheme.dime.sm))
|
||||||
Icon(
|
}
|
||||||
imageVector = Icons.Default.Search,
|
}
|
||||||
contentDescription = "Search",
|
DrugListContent(
|
||||||
modifier = Modifier.size(18.dp)
|
drugsData = state.drugSearchState.drugsData,
|
||||||
)
|
onDrugClick = onDrugClick
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
else if (state.darmanState.getDarmani != null ){
|
||||||
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
|
Column {
|
||||||
PrimaryButton(
|
|
||||||
text = "جستجو",
|
Row(
|
||||||
height = 40,
|
modifier = Modifier.fillMaxWidth(),
|
||||||
isLoading = state.drugSearchState.isLoading,
|
horizontalArrangement = Arrangement.End
|
||||||
onClick = {
|
) {
|
||||||
if (search.isNotBlank()) {
|
Text("لیست گروه های دارویی" , style = MaterialTheme.typography.labelLarge.copy(
|
||||||
onSearch(search)
|
color = MaterialTheme.colorScheme.primary
|
||||||
}
|
) ,modifier= Modifier.padding(MaterialTheme.dime.md))
|
||||||
}
|
}
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
|
DarmanContent(
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
darmanData = state.darmanState.getDarmani,
|
||||||
|
onDarmanClick = { value ->
|
||||||
// Always draw the data if available
|
onSearch(value)
|
||||||
if (state.drugSearchState.drugsData != null && !state.showDarmanList) {
|
},
|
||||||
Column {
|
onRetryClick = {
|
||||||
Row(
|
onRetry()
|
||||||
horizontalArrangement = Arrangement.End,
|
}
|
||||||
modifier = Modifier.fillMaxWidth()
|
)
|
||||||
) {
|
}
|
||||||
TextButton(
|
} else {
|
||||||
onClick = {
|
EmptyStateContent()
|
||||||
onRetry()
|
}
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text("مشاهده تمامی گروه های دارویی")
|
|
||||||
Icon(
|
// Draw the loading dialog on top if needed
|
||||||
imageVector = Icons.Default.KeyboardArrowRight,
|
if (state.drugSearchState.isLoading || state.drugDetailState.isLoading || state.darmanState.isLoading) {
|
||||||
contentDescription = "back"
|
CustomModalDialog(
|
||||||
)
|
onDismissRequest = { /* maybe disable dismiss while loading */ },
|
||||||
Spacer(modifier = Modifier.width(MaterialTheme.dime.sm))
|
content = {
|
||||||
}
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
}
|
Loading(color = MaterialTheme.colorScheme.primary)
|
||||||
DrugListContent(
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
|
||||||
drugsData = state.drugSearchState.drugsData,
|
Text("در حال جستجو..." , textAlign = TextAlign.Right)
|
||||||
onDrugClick = onDrugClick
|
}
|
||||||
)
|
},
|
||||||
}
|
// modifier = Modifier
|
||||||
}
|
// .matchParentSize()
|
||||||
else if (state.darmanState.getDarmani != null ){
|
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
|
||||||
Column {
|
)
|
||||||
|
}
|
||||||
Row(
|
}
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
horizontalArrangement = Arrangement.End
|
|
||||||
) {
|
}
|
||||||
Text("لیست گروه های دارویی" , style = MaterialTheme.typography.labelLarge.copy(
|
}
|
||||||
color = MaterialTheme.colorScheme.primary
|
}
|
||||||
) ,modifier= Modifier.padding(MaterialTheme.dime.md))
|
|
||||||
}
|
|
||||||
|
|
||||||
DarmanContent(
|
|
||||||
darmanData = state.darmanState.getDarmani,
|
|
||||||
onDarmanClick = { value ->
|
|
||||||
onSearch(value)
|
|
||||||
},
|
|
||||||
onRetryClick = {
|
|
||||||
onRetry()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EmptyStateContent()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Draw the loading dialog on top if needed
|
|
||||||
if (state.drugSearchState.isLoading || state.drugDetailState.isLoading || state.darmanState.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))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,185 +1,207 @@
|
|||||||
package com.approagency.drug.presentation.screens
|
package com.approagency.drug.presentation.screens
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import android.R.attr.onClick
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.ui.text.style.TextDirection
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.runtime.collectAsState
|
||||||
import com.approagency.drug.domain.model.TestGroup
|
import androidx.compose.runtime.getValue
|
||||||
import com.approagency.drug.presentation.common.CustomBox
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import com.approagency.drug.presentation.common.CustomModalDialog
|
import androidx.compose.runtime.remember
|
||||||
import com.approagency.drug.presentation.common.Loading
|
import androidx.compose.runtime.setValue
|
||||||
import com.approagency.drug.presentation.common.PrimaryButton
|
import androidx.compose.ui.AbsoluteAlignment
|
||||||
import com.approagency.drug.presentation.viewModel.LabViewModel
|
import androidx.compose.ui.Alignment
|
||||||
import com.vada.caller.ui.theme.dime
|
import androidx.compose.ui.Modifier
|
||||||
import kotlinx.coroutines.flow.Flow
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import kotlinx.coroutines.flow.count
|
import androidx.compose.ui.text.style.TextDirection
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
@Composable
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
fun LabScreen(
|
import com.approagency.drug.domain.model.TestItem
|
||||||
modifier: Modifier = Modifier,
|
import com.approagency.drug.presentation.common.CustomBox
|
||||||
viewModel: LabViewModel = koinViewModel()
|
import com.approagency.drug.presentation.common.CustomModalDialog
|
||||||
) {
|
import com.approagency.drug.presentation.common.CustomTextFilled
|
||||||
val testGroupsState by viewModel.testGroups.collectAsState()
|
import com.approagency.drug.presentation.common.Loading
|
||||||
val isLoading = testGroupsState.isLoading
|
import com.approagency.drug.presentation.common.PrimaryButton
|
||||||
val error = testGroupsState.error
|
import com.approagency.drug.presentation.components.RetryContent
|
||||||
val testGroups = testGroupsState.testGroup
|
import com.approagency.drug.presentation.components.SearchResultsContent
|
||||||
|
import com.approagency.drug.presentation.components.TestDetailSheet
|
||||||
Column(modifier = modifier.fillMaxSize()) {
|
import com.approagency.drug.presentation.components.TestGroupItemContent
|
||||||
if (isLoading) {
|
import com.approagency.drug.presentation.viewModel.LabViewModel
|
||||||
CustomModalDialog(
|
import com.vada.caller.ui.theme.dime
|
||||||
onDismissRequest = { /* maybe disable dismiss while loading */ },
|
import kotlinx.coroutines.flow.Flow
|
||||||
content = {
|
import kotlinx.coroutines.flow.count
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
import org.koin.androidx.compose.koinViewModel
|
||||||
Loading(color = MaterialTheme.colorScheme.primary)
|
|
||||||
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
|
@Composable
|
||||||
Text("در حال جستجو..." , textAlign = TextAlign.Right)
|
fun LabScreen(
|
||||||
}
|
modifier: Modifier = Modifier,
|
||||||
},
|
viewModel: LabViewModel = koinViewModel()
|
||||||
// modifier = Modifier
|
) {
|
||||||
// .matchParentSize()
|
val testGroupsState by viewModel.testGroups.collectAsState()
|
||||||
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
|
val testItemsState by viewModel.testItems.collectAsState()
|
||||||
)
|
val searchResultsState by viewModel.searchResults.collectAsState()
|
||||||
}
|
|
||||||
else if(error != null){
|
val isLoading = testGroupsState.isLoading || testItemsState.isLoading || searchResultsState.isLoading
|
||||||
CustomBox(
|
val error = testGroupsState.error ?: searchResultsState.error
|
||||||
child = {
|
val testGroups = testGroupsState.testGroup
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
var selectedGroup by remember { mutableStateOf<TestGroup?>(null) }
|
||||||
modifier = Modifier.padding(MaterialTheme.dime.md)
|
var selectedItem by remember { mutableStateOf<TestItem?>(null) }
|
||||||
) {
|
var showSheet by remember { mutableStateOf(false) }
|
||||||
Text(
|
var searchText by remember { mutableStateOf("") }
|
||||||
text = "خطا در دریافت اطلاعات",
|
Column(modifier = modifier.fillMaxSize().padding(horizontal = MaterialTheme.dime.md)
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
.padding(top = MaterialTheme.dime.md),) {
|
||||||
color = MaterialTheme.colorScheme.error
|
CustomTextFilled(
|
||||||
)
|
value = searchText,
|
||||||
Text(
|
onValueChange = {
|
||||||
text = error,
|
searchText = it
|
||||||
style = MaterialTheme.typography.bodySmall,
|
viewModel.updateSearchQuery(it)
|
||||||
modifier = Modifier.padding(vertical = 8.dp)
|
},
|
||||||
)
|
onSearch = { query ->
|
||||||
PrimaryButton(
|
if (query.isNotBlank()) {
|
||||||
text = "تلاش مجدد",
|
viewModel.updateSearchQuery(query)
|
||||||
height = 40,
|
}
|
||||||
isLoading = false,
|
},
|
||||||
onClick = { viewModel.loadTestGroups() }
|
placeholder = "جستجو در گروهها و آیتمهای آزمایشگاهی",
|
||||||
)
|
showClearButton = true,
|
||||||
}
|
showSearchButton = true,
|
||||||
}
|
autoSearch = true,
|
||||||
)
|
height = 45
|
||||||
}
|
)
|
||||||
else if (testGroups != null){
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
|
||||||
TestGroupsList(
|
PrimaryButton(
|
||||||
testGroupsFlow = testGroups,
|
text = "جستجو",
|
||||||
viewModel = viewModel
|
height = 40,
|
||||||
)
|
isLoading =false,
|
||||||
} else {
|
onClick = {}
|
||||||
Box(
|
)
|
||||||
modifier = Modifier.fillMaxSize(),
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.xs))
|
||||||
contentAlignment = Alignment.Center
|
if (searchResultsState.query.isNotBlank()) {
|
||||||
) {
|
// Search Results Section
|
||||||
Text("هیچ دادهای موجود نیست")
|
SearchResultsContent(
|
||||||
}
|
searchResult = searchResultsState.searchResults,
|
||||||
}
|
isLoading = searchResultsState.isLoading,
|
||||||
|
error = searchResultsState.error,
|
||||||
}
|
onGroupClick = { group ->
|
||||||
}
|
selectedGroup = group
|
||||||
|
viewModel.getItemByGroupId(group.id)
|
||||||
@Composable
|
showSheet = true
|
||||||
fun TestGroupsList(
|
},
|
||||||
testGroupsFlow: Flow<List<TestGroup>>,
|
onItemClick = { item ->
|
||||||
viewModel: LabViewModel
|
selectedItem = item
|
||||||
) {
|
// You can show item detail sheet here
|
||||||
val testGroups by testGroupsFlow.collectAsState(initial = emptyList())
|
// For now, we'll show the parent group
|
||||||
|
viewModel.getItemByGroupId(item.groupId)
|
||||||
if (testGroups.isEmpty()) {
|
showSheet = true
|
||||||
CustomModalDialog(
|
},
|
||||||
onDismissRequest = { /* maybe disable dismiss while loading */ },
|
onClearSearch = {
|
||||||
content = {
|
searchText = ""
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
viewModel.clearSearch()
|
||||||
Loading(color = MaterialTheme.colorScheme.primary)
|
viewModel.loadTestGroups()
|
||||||
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
|
}
|
||||||
Text("در حال جستجو..." , textAlign = TextAlign.Right)
|
)
|
||||||
}
|
} else if(error != null){
|
||||||
},
|
RetryContent(modifier = modifier , error = error , onClick = {
|
||||||
// modifier = Modifier
|
viewModel.loadTestGroups()
|
||||||
// .matchParentSize()
|
})
|
||||||
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
|
}
|
||||||
)
|
else if (testGroups != null){
|
||||||
} else {
|
TestGroupsList(
|
||||||
LazyColumn(
|
testGroupsFlow = testGroups,
|
||||||
modifier = Modifier.fillMaxSize()
|
viewModel = viewModel,
|
||||||
) {
|
onGroupClick = { group ->
|
||||||
items(testGroups) { group ->
|
selectedGroup = group
|
||||||
TestGroupItem(
|
viewModel.getItemByGroupId(group.id)
|
||||||
group = group,
|
showSheet = true
|
||||||
onClick = {
|
}
|
||||||
// Navigate to test items of this group
|
)
|
||||||
// viewModel.loadTestItemsByGroup(group.id)
|
}
|
||||||
}
|
else {
|
||||||
)
|
Box(
|
||||||
}
|
modifier = Modifier.fillMaxSize(),
|
||||||
}
|
contentAlignment = Alignment.Center
|
||||||
}
|
) {
|
||||||
}
|
Text("هیچ دادهای موجود نیست")
|
||||||
|
}
|
||||||
@Composable
|
}
|
||||||
fun TestGroupItem(
|
|
||||||
group: TestGroup,
|
}
|
||||||
onClick: () -> Unit
|
if (isLoading) {
|
||||||
) {
|
CustomModalDialog(
|
||||||
CustomBox (
|
onDismissRequest = { /* maybe disable dismiss while loading */ },
|
||||||
modifier = Modifier
|
content = {
|
||||||
.padding(bottom = MaterialTheme.dime.sm)
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
.clickable(
|
Loading(color = MaterialTheme.colorScheme.primary)
|
||||||
indication = null,
|
Spacer(modifier = Modifier.height(MaterialTheme.dime.sm))
|
||||||
interactionSource = remember { MutableInteractionSource() }
|
Text("در حال جستجو..." , textAlign = TextAlign.Right)
|
||||||
) {
|
}
|
||||||
onClick()
|
},
|
||||||
},
|
// modifier = Modifier
|
||||||
) {
|
// .matchParentSize()
|
||||||
Column (
|
// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f))
|
||||||
verticalArrangement = Arrangement.Center,
|
)
|
||||||
horizontalAlignment = Alignment.Start
|
}
|
||||||
) {
|
if (showSheet && selectedGroup != null) {
|
||||||
Text(
|
TestDetailSheet(
|
||||||
text = group.fname ?: group.ename ?: "بدون نام",
|
state = testItemsState,
|
||||||
style = MaterialTheme.typography.titleMedium.copy(
|
testGroup = selectedGroup!!,
|
||||||
textAlign = TextAlign.Right,
|
onDismiss = { showSheet = false }
|
||||||
textDirection = TextDirection.Rtl
|
)
|
||||||
),
|
}
|
||||||
modifier = Modifier.padding(vertical = MaterialTheme.dime.xs)
|
}
|
||||||
)
|
|
||||||
group.detail?.let {
|
@Composable
|
||||||
Text(
|
fun TestGroupsList(
|
||||||
text = it,
|
testGroupsFlow: Flow<List<TestGroup>>,
|
||||||
style = MaterialTheme.typography.bodySmall.copy(
|
viewModel: LabViewModel,
|
||||||
textAlign = TextAlign.Right ,
|
onGroupClick: (TestGroup) -> Unit
|
||||||
textDirection = TextDirection.Rtl
|
) {
|
||||||
),
|
val testGroups by testGroupsFlow.collectAsState(initial = emptyList())
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
modifier = Modifier.padding(horizontal = MaterialTheme.dime.lg)
|
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()
|
||||||
|
.fillMaxSize().padding( vertical =MaterialTheme.dime.md )
|
||||||
|
) {
|
||||||
|
items(testGroups) { group ->
|
||||||
|
TestGroupItemContent(
|
||||||
|
group = group,
|
||||||
|
onClick = { onGroupClick(group) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,128 +1,126 @@
|
|||||||
package com.approagency.drug.presentation.screens
|
package com.approagency.drug.presentation.screens
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.graphics.drawable.Icon
|
import android.graphics.drawable.Icon
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.only
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Build
|
import androidx.compose.material.icons.filled.Build
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.NavigationBar
|
import androidx.compose.material3.NavigationBar
|
||||||
import androidx.compose.material3.NavigationBarDefaults
|
import androidx.compose.material3.NavigationBarDefaults
|
||||||
import androidx.compose.material3.NavigationBarItem
|
import androidx.compose.material3.NavigationBarItem
|
||||||
import androidx.compose.material3.NavigationBarItemDefaults
|
import androidx.compose.material3.NavigationBarItemDefaults
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.ScaffoldDefaults
|
import androidx.compose.material3.ScaffoldDefaults
|
||||||
import androidx.compose.material3.SegmentedButtonDefaults.Icon
|
import androidx.compose.material3.SegmentedButtonDefaults.Icon
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.LayoutDirection
|
import androidx.compose.ui.unit.LayoutDirection
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
@SuppressLint("ConfigurationScreenWidthHeight")
|
@SuppressLint("ConfigurationScreenWidthHeight")
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RootScreen(navHostController: NavHostController, modifier: Modifier){
|
fun RootScreen(navHostController: NavHostController, modifier: Modifier){
|
||||||
val pages = listOf(
|
val pages = listOf(
|
||||||
"دارو" to Icons.Default.Search,
|
"دارو" to Icons.Default.Search,
|
||||||
"آزمایش" to Icons.Default.Build
|
"آزمایش" to Icons.Default.Build
|
||||||
)
|
)
|
||||||
var seletedTab by remember { mutableStateOf(0) }
|
var seletedTab by remember { mutableStateOf(0) }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val activity = context as Activity
|
val activity = context as Activity
|
||||||
|
|
||||||
Scaffold (
|
Scaffold (
|
||||||
contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
|
contentWindowInsets = ScaffoldDefaults.contentWindowInsets,
|
||||||
topBar = { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl){
|
topBar = { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl){
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||||
titleContentColor = MaterialTheme.colorScheme.primary,
|
titleContentColor = MaterialTheme.colorScheme.primary,
|
||||||
),
|
),
|
||||||
title = {
|
title = {
|
||||||
Text("جستجوی دارویی" , textAlign = TextAlign.Right , style = MaterialTheme.typography.titleLarge , fontWeight = FontWeight.W700 )
|
Text("دستیار سلامت" , textAlign = TextAlign.Right , style = MaterialTheme.typography.titleLarge , fontWeight = FontWeight.W700 )
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}},
|
}},
|
||||||
// modifier = modifier.fillMaxSize(),
|
bottomBar = {
|
||||||
bottomBar = {
|
NavigationBar(
|
||||||
NavigationBar(
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
modifier = Modifier
|
||||||
containerColor = MaterialTheme.colorScheme.surface,
|
.fillMaxWidth()
|
||||||
modifier = Modifier
|
.height(110.dp).padding(bottom = 2.dp).clip(MaterialTheme.shapes.extraLarge),
|
||||||
.fillMaxWidth()
|
tonalElevation = 8.dp, // subtle shadow
|
||||||
.height(110.dp).padding(bottom = 2.dp).clip(MaterialTheme.shapes.extraLarge),
|
windowInsets = WindowInsets.navigationBars
|
||||||
tonalElevation = 8.dp, // subtle shadow
|
.only(WindowInsetsSides.Bottom)
|
||||||
windowInsets = WindowInsets.navigationBars
|
) {
|
||||||
.only(WindowInsetsSides.Bottom)
|
pages.forEachIndexed { index, item ->
|
||||||
) {
|
val selected = seletedTab == index
|
||||||
pages.forEachIndexed { index, item ->
|
|
||||||
val selected = seletedTab == index
|
NavigationBarItem(
|
||||||
|
colors = NavigationBarItemDefaults.colors(
|
||||||
NavigationBarItem(
|
selectedIconColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
colors = NavigationBarItemDefaults.colors(
|
selectedTextColor = MaterialTheme.colorScheme.primary,
|
||||||
selectedIconColor = MaterialTheme.colorScheme.onPrimary,
|
indicatorColor = MaterialTheme.colorScheme.primary,
|
||||||
selectedTextColor = MaterialTheme.colorScheme.primary,
|
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
indicatorColor = MaterialTheme.colorScheme.primary,
|
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
),
|
||||||
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
|
selected = selected,
|
||||||
),
|
onClick = { seletedTab = index },
|
||||||
selected = selected,
|
|
||||||
onClick = { seletedTab = index },
|
icon = {
|
||||||
|
Icon(
|
||||||
icon = {
|
imageVector = item.second,
|
||||||
Icon(
|
contentDescription = item.first,
|
||||||
imageVector = item.second,
|
modifier = Modifier.size(16.dp)
|
||||||
contentDescription = item.first,
|
)
|
||||||
modifier = Modifier.size(16.dp)
|
},
|
||||||
)
|
|
||||||
},
|
label = {
|
||||||
|
Text(
|
||||||
label = {
|
item.first,
|
||||||
Text(
|
style = MaterialTheme.typography.labelMedium
|
||||||
item.first,
|
)
|
||||||
style = MaterialTheme.typography.labelMedium
|
},
|
||||||
)
|
|
||||||
},
|
alwaysShowLabel = true
|
||||||
|
)
|
||||||
alwaysShowLabel = true
|
}
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
}
|
) {
|
||||||
}
|
padding -> when (seletedTab) {
|
||||||
) {
|
0 -> HomeScreen( navController = navHostController,Modifier.padding(padding).consumeWindowInsets(padding))
|
||||||
padding -> when (seletedTab) {
|
1 -> LabScreen(Modifier.padding(padding))
|
||||||
0 -> HomeScreen( navController = navHostController,Modifier.padding(padding).consumeWindowInsets(padding))
|
}
|
||||||
1 -> LabScreen(Modifier.padding(padding))
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,165 +1,187 @@
|
|||||||
package com.approagency.drug.presentation.viewModel
|
package com.approagency.drug.presentation.viewModel
|
||||||
|
|
||||||
import android.net.http.HttpException
|
import android.net.http.HttpException
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.approagency.drug.data.dto.DarmanModel
|
import com.approagency.drug.data.dto.DarmanModel
|
||||||
import com.approagency.drug.data.dto.DrugDetail
|
import com.approagency.drug.data.dto.DrugDetail
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.data.dto.DrugModels
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import com.approagency.drug.domain.model.DrugSearchParams
|
import com.approagency.drug.domain.model.DrugSearchParams
|
||||||
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.presentation.screens.HomeScreen
|
import com.approagency.drug.presentation.screens.HomeScreen
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import com.approagency.drug.utils.retryWithBackoff
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
class HomeViewModel (
|
|
||||||
private val getDrugSearchUseCase: GetDrugSearchUseCase,
|
class HomeViewModel (
|
||||||
private val getDrugDetailUseCase: GetDrugDetailUseCase,
|
private val getDrugSearchUseCase: GetDrugSearchUseCase,
|
||||||
private val getDarmanUseCase: GetDarmanUseCase
|
private val getDrugDetailUseCase: GetDrugDetailUseCase,
|
||||||
): ViewModel() {
|
private val getDarmanUseCase: GetDarmanUseCase
|
||||||
private val _uiState = MutableStateFlow(CombineState())
|
): ViewModel() {
|
||||||
val uiState : StateFlow<CombineState> = _uiState
|
private val _uiState = MutableStateFlow(CombineState())
|
||||||
|
val uiState : StateFlow<CombineState> = _uiState
|
||||||
// Add SharedFlow for one-time events
|
|
||||||
private val _event = MutableSharedFlow<HomeEvent>()
|
// Add SharedFlow for one-time events
|
||||||
val event = _event.asSharedFlow()
|
private val _event = MutableSharedFlow<HomeEvent>()
|
||||||
init {
|
val event = _event.asSharedFlow()
|
||||||
getDarmani()
|
init {
|
||||||
}
|
getDarmani()
|
||||||
fun getDarmani(){
|
}
|
||||||
_uiState.update { it.copy(darmanState = it.darmanState.copy(
|
fun getDarmani() {
|
||||||
isLoading = true
|
_uiState.update {
|
||||||
) , showDarmanList = true ) }
|
it.copy(
|
||||||
viewModelScope.launch {
|
darmanState = it.darmanState.copy(isLoading = true),
|
||||||
try {
|
showDarmanList = true
|
||||||
val result = getDarmanUseCase.invoke()
|
)
|
||||||
_uiState.update {
|
}
|
||||||
it.copy(
|
viewModelScope.launch {
|
||||||
darmanState = it.darmanState.copy(
|
retryWithBackoff(
|
||||||
isLoading = false,
|
maxRetries = 3,
|
||||||
getDarmani = result
|
onRetry = { attempt, delay ->
|
||||||
)
|
// Optional: log retry attempt
|
||||||
)
|
println("Retrying getDarmani, attempt $attempt, delay $delay ms")
|
||||||
}
|
}
|
||||||
} catch (e: HttpException){
|
) {
|
||||||
handleError(e.message)
|
getDarmanUseCase.invoke()
|
||||||
}
|
}.fold(
|
||||||
}
|
onSuccess = { result ->
|
||||||
}
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
fun searchDrugs(drugSearchParams: DrugSearchParams) {
|
darmanState = it.darmanState.copy(
|
||||||
_uiState.update { it.copy(drugSearchState = it.drugSearchState.copy(
|
isLoading = false,
|
||||||
isLoading = true
|
getDarmani = result
|
||||||
) , showDarmanList = false) }
|
)
|
||||||
viewModelScope.launch {
|
)
|
||||||
try {
|
}
|
||||||
val result = getDrugSearchUseCase.invoke(drugSearchParams)
|
},
|
||||||
_uiState.update { it.copy(
|
onFailure = { error ->
|
||||||
drugSearchState = it.drugSearchState.copy(
|
handleError(error.message ?: "Unknown error")
|
||||||
isLoading = false,
|
_uiState.update {
|
||||||
drugsData = result
|
it.copy(
|
||||||
)
|
darmanState = it.darmanState.copy(
|
||||||
|
isLoading = false,
|
||||||
) }
|
getDarmani = Result.failure(error)
|
||||||
} catch (e: HttpException) {
|
)
|
||||||
handleError(e.message)
|
)
|
||||||
}catch (e: Exception) {
|
}
|
||||||
handleError(e.localizedMessage ?: "Unknown error")
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun drugDetail(cod: Int , allDrugs:Result<DrugListResponse?>?) {
|
fun searchDrugs(drugSearchParams: DrugSearchParams) {
|
||||||
_uiState.update { it.copy(drugDetailState = it.drugDetailState.copy(
|
_uiState.update { it.copy(drugSearchState = it.drugSearchState.copy(
|
||||||
isLoading = true
|
isLoading = true
|
||||||
) , showDarmanList = false) }
|
) , showDarmanList = false) }
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
val result = getDrugDetailUseCase.invoke(cod)
|
val result = getDrugSearchUseCase.invoke(drugSearchParams)
|
||||||
_uiState.update {
|
_uiState.update { it.copy(
|
||||||
it.copy(
|
drugSearchState = it.drugSearchState.copy(
|
||||||
drugDetailState = it.drugDetailState.copy(
|
isLoading = false,
|
||||||
isLoading = false,
|
drugsData = result
|
||||||
drugDetail = result,
|
)
|
||||||
isDetailVisible = true
|
|
||||||
),
|
) }
|
||||||
drugSearchState = it.drugSearchState.copy(
|
} catch (e: HttpException) {
|
||||||
isLoading = false,
|
handleError(e.message)
|
||||||
drugsData = allDrugs
|
}catch (e: Exception) {
|
||||||
)
|
handleError(e.localizedMessage ?: "Unknown error")
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
println(result)
|
}
|
||||||
} catch (e: HttpException) {
|
|
||||||
handleError(e.message)
|
fun drugDetail(cod: Int , allDrugs:Result<DrugListResponse?>?) {
|
||||||
}catch (e: Exception) {
|
_uiState.update { it.copy(drugDetailState = it.drugDetailState.copy(
|
||||||
handleError(e.localizedMessage ?: "Unknown error")
|
isLoading = true
|
||||||
}
|
) , showDarmanList = false) }
|
||||||
}
|
viewModelScope.launch {
|
||||||
}
|
try {
|
||||||
fun closeDetail() {
|
val result = getDrugDetailUseCase.invoke(cod)
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
drugDetailState = it.drugDetailState.copy(
|
drugDetailState = it.drugDetailState.copy(
|
||||||
isDetailVisible = false,
|
isLoading = false,
|
||||||
drugDetail = null // optional, depends if you want to keep last detail cached
|
drugDetail = result,
|
||||||
)
|
isDetailVisible = true
|
||||||
)
|
),
|
||||||
}
|
drugSearchState = it.drugSearchState.copy(
|
||||||
}
|
isLoading = false,
|
||||||
|
drugsData = allDrugs
|
||||||
private suspend fun handleError(message: String?) {
|
)
|
||||||
_uiState.update { it.copy(drugSearchState = it.drugSearchState.copy(
|
)
|
||||||
isLoading = false
|
}
|
||||||
) , drugDetailState = it.drugDetailState.copy(
|
println(result)
|
||||||
isLoading = false
|
} catch (e: HttpException) {
|
||||||
)) }
|
handleError(e.message)
|
||||||
_event.emit(HomeEvent.ShowError(message ?: "Error occurred"))
|
}catch (e: Exception) {
|
||||||
}
|
handleError(e.localizedMessage ?: "Unknown error")
|
||||||
|
}
|
||||||
// Clear error when consumed
|
}
|
||||||
fun clearError() {
|
}
|
||||||
// _uiState.update { it.copy(error = null) }
|
fun closeDetail() {
|
||||||
}
|
_uiState.update {
|
||||||
}
|
it.copy(
|
||||||
|
drugDetailState = it.drugDetailState.copy(
|
||||||
|
isDetailVisible = false,
|
||||||
sealed class HomeEvent {
|
drugDetail = null // optional, depends if you want to keep last detail cached
|
||||||
data class ShowError(val message: String) : HomeEvent()
|
)
|
||||||
data class ShowDetailDrug(val drugDetail:Result<DrugModels?>?): HomeEvent()
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
data class CombineState(
|
|
||||||
val drugSearchState: DrugSearchState = DrugSearchState() ,
|
private suspend fun handleError(message: String?) {
|
||||||
val drugDetailState: DrugDetailState =DrugDetailState(),
|
_uiState.update { it.copy(drugSearchState = it.drugSearchState.copy(
|
||||||
val darmanState: GetDarmaniState = GetDarmaniState(),
|
isLoading = false
|
||||||
val showDarmanList: Boolean = true
|
) , drugDetailState = it.drugDetailState.copy(
|
||||||
)
|
isLoading = false
|
||||||
|
)) }
|
||||||
data class DrugSearchState(
|
_event.emit(HomeEvent.ShowError(message ?: "Error occurred"))
|
||||||
val isLoading: Boolean = false,
|
}
|
||||||
val drugsData: Result<DrugListResponse?>? = null,
|
|
||||||
val drugDetail: Result<DrugModels?>? = null,
|
// Clear error when consumed
|
||||||
val error:String? = null
|
fun clearError() {
|
||||||
)
|
// _uiState.update { it.copy(error = null) }
|
||||||
|
}
|
||||||
data class DrugDetailState(
|
}
|
||||||
val isLoading: Boolean = false,
|
|
||||||
val drugDetail:Result<DrugModels?>? = null,
|
|
||||||
val isDetailVisible: Boolean = false,
|
sealed class HomeEvent {
|
||||||
val error:String? = null
|
data class ShowError(val message: String) : HomeEvent()
|
||||||
)
|
data class ShowDetailDrug(val drugDetail:Result<DrugModels?>?): HomeEvent()
|
||||||
|
}
|
||||||
data class GetDarmaniState(
|
|
||||||
val isLoading: Boolean = false,
|
data class CombineState(
|
||||||
val getDarmani:Result<DarmanModel?>? = null,
|
val drugSearchState: DrugSearchState = DrugSearchState() ,
|
||||||
val error:String? = null
|
val drugDetailState: DrugDetailState =DrugDetailState(),
|
||||||
)
|
val darmanState: GetDarmaniState = GetDarmaniState(),
|
||||||
|
val showDarmanList: Boolean = true
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DrugSearchState(
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val drugsData: Result<DrugListResponse?>? = null,
|
||||||
|
val drugDetail: Result<DrugModels?>? = null,
|
||||||
|
val error:String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class DrugDetailState(
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val drugDetail:Result<DrugModels?>? = null,
|
||||||
|
val isDetailVisible: Boolean = false,
|
||||||
|
val error:String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class GetDarmaniState(
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val getDarmani:Result<DarmanModel?>? = null,
|
||||||
|
val error:String? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,94 +1,174 @@
|
|||||||
package com.approagency.drug.presentation.viewModel
|
package com.approagency.drug.presentation.viewModel
|
||||||
|
|
||||||
import android.net.http.HttpException
|
import android.net.http.HttpException
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.approagency.drug.data.dto.DrugListResponse
|
import com.approagency.drug.data.dto.DrugListResponse
|
||||||
import com.approagency.drug.data.dto.DrugModels
|
import com.approagency.drug.data.dto.DrugModels
|
||||||
import com.approagency.drug.domain.model.TestGroup
|
import com.approagency.drug.domain.model.TestGroup
|
||||||
import com.approagency.drug.domain.model.TestItem
|
import com.approagency.drug.domain.model.TestItem
|
||||||
import com.approagency.drug.domain.usecase.GetTestGroupUseCase
|
import com.approagency.drug.domain.usecase.GetTestGroupUseCase
|
||||||
import com.approagency.drug.domain.usecase.GetTestItemByGroupId
|
import com.approagency.drug.domain.usecase.GetTestItemByGroupId
|
||||||
import kotlinx.coroutines.flow.Flow
|
import com.approagency.drug.domain.usecase.SearchTestsUseCase
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
class LabViewModel (
|
import kotlinx.coroutines.flow.update
|
||||||
private val getTestGroupUseCase: GetTestGroupUseCase,
|
import kotlinx.coroutines.launch
|
||||||
private val getTestItemByGroupId: GetTestItemByGroupId
|
|
||||||
) : ViewModel(){
|
class LabViewModel (
|
||||||
private val _testGroups = MutableStateFlow(TestGroupItem())
|
private val getTestGroupUseCase: GetTestGroupUseCase,
|
||||||
val testGroups: StateFlow<TestGroupItem> = _testGroups.asStateFlow()
|
private val getTestItemByGroupId: GetTestItemByGroupId,
|
||||||
|
private val searchTestsUseCase: SearchTestsUseCase
|
||||||
private val _testItems = MutableStateFlow(TestItemState())
|
) : ViewModel(){
|
||||||
val testItems: StateFlow<TestItemState> = _testItems.asStateFlow()
|
private val _testGroups = MutableStateFlow(TestGroupItem())
|
||||||
|
val testGroups: StateFlow<TestGroupItem> = _testGroups.asStateFlow()
|
||||||
init {
|
|
||||||
loadTestGroups()
|
private val _testItems = MutableStateFlow(TestItemState())
|
||||||
}
|
val testItems: StateFlow<TestItemState> = _testItems.asStateFlow()
|
||||||
|
|
||||||
fun loadTestGroups(){
|
|
||||||
_testGroups.update {
|
private val _searchResults = MutableStateFlow(SearchResultState())
|
||||||
it.copy(
|
val searchResults: StateFlow<SearchResultState> = _searchResults.asStateFlow()
|
||||||
isLoading = true
|
|
||||||
)
|
private val searchQuery = MutableStateFlow("")
|
||||||
}
|
|
||||||
viewModelScope.launch {
|
init {
|
||||||
try {
|
loadTestGroups()
|
||||||
val result = getTestGroupUseCase.invoke();
|
setupSearch()
|
||||||
_testGroups.update {
|
}
|
||||||
it.copy(
|
private fun setupSearch() {
|
||||||
isLoading = false,
|
viewModelScope.launch {
|
||||||
testGroup = result,
|
searchQuery
|
||||||
)
|
.debounce(1000) // Wait 500ms after user stops typing
|
||||||
}
|
.distinctUntilChanged()
|
||||||
} catch (e:HttpException){
|
.collect { query ->
|
||||||
handleError(e.message)
|
if (query.isNotBlank()) {
|
||||||
}
|
performSearch(query)
|
||||||
|
} else {
|
||||||
}
|
_searchResults.update {
|
||||||
}
|
SearchResultState(
|
||||||
|
isLoading = false,
|
||||||
fun getItemByGroupId(id:Int){
|
searchResults = null,
|
||||||
_testItems.update { it.copy(isLoading = true) }
|
query = ""
|
||||||
viewModelScope.launch {
|
)
|
||||||
try {
|
}
|
||||||
val result = getTestItemByGroupId.invoke(id);
|
}
|
||||||
_testItems.update {
|
}
|
||||||
it.copy(
|
}
|
||||||
isLoading = false,
|
}
|
||||||
testItem = result
|
|
||||||
)
|
fun updateSearchQuery(query: String) {
|
||||||
}
|
searchQuery.value = query
|
||||||
} catch (e:HttpException){
|
_searchResults.update {
|
||||||
handleError(e.message)
|
it.copy(
|
||||||
}
|
isLoading = query.isNotBlank(),
|
||||||
}
|
query = query
|
||||||
}
|
)
|
||||||
private suspend fun handleError(message: String?) {
|
}
|
||||||
_testGroups.update {
|
}
|
||||||
it.copy(
|
|
||||||
isLoading = false,
|
private fun performSearch(query: String) {
|
||||||
testGroup = null,
|
viewModelScope.launch {
|
||||||
error = message
|
try {
|
||||||
)
|
val result = searchTestsUseCase.invoke(query)
|
||||||
}
|
result.collect { (groups, items) ->
|
||||||
}
|
_searchResults.update {
|
||||||
|
it.copy(
|
||||||
}
|
isLoading = false,
|
||||||
|
searchResults = SearchResult(groups = groups, items = items),
|
||||||
data class TestGroupItem(
|
error = null
|
||||||
val isLoading: Boolean = false,
|
)
|
||||||
val testGroup: Flow<List<TestGroup>>? = null,
|
}
|
||||||
val error:String? = null
|
}
|
||||||
)
|
} catch (e: Exception) {
|
||||||
|
_searchResults.update {
|
||||||
data class TestItemState(
|
it.copy(
|
||||||
val isLoading: Boolean = false,
|
isLoading = false,
|
||||||
val testItem: Flow<List<TestItem>>? = null,
|
searchResults = null,
|
||||||
val error:String? = null
|
error = e.message ?: "خطا در جستجو"
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 clearSearch() {
|
||||||
|
searchQuery.value = ""
|
||||||
|
_searchResults.update {
|
||||||
|
SearchResultState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
)
|
||||||
|
data class SearchResultState(
|
||||||
|
val isLoading: Boolean = false,
|
||||||
|
val searchResults: SearchResult? = null,
|
||||||
|
val error: String? = null,
|
||||||
|
val query: String = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
data class SearchResult(
|
||||||
|
val groups: List<TestGroup>,
|
||||||
|
val items: List<TestItem>
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
package com.vada.caller.ui.theme
|
package com.vada.caller.ui.theme
|
||||||
|
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
|
||||||
data class Dime (
|
data class Dime (
|
||||||
val xxs: Dp = 2.dp,
|
val xxs: Dp = 2.dp,
|
||||||
val xs: Dp = 4.dp,
|
val xs: Dp = 4.dp,
|
||||||
val sm: Dp = 8.dp,
|
val sm: Dp = 8.dp,
|
||||||
val md: Dp = 12.dp,
|
val md: Dp = 12.dp,
|
||||||
val lg: Dp = 16.dp,
|
val lg: Dp = 16.dp,
|
||||||
val xl: Dp = 20.dp,
|
val xl: Dp = 20.dp,
|
||||||
val xxl: Dp = 24.dp,
|
val xxl: Dp = 24.dp,
|
||||||
|
|
||||||
|
|
||||||
val iconSm: Dp = 20.dp,
|
val iconSm: Dp = 20.dp,
|
||||||
val iconMd: Dp = 24.dp,
|
val iconMd: Dp = 24.dp,
|
||||||
val iconLg: Dp = 32.dp,
|
val iconLg: Dp = 32.dp,
|
||||||
)
|
)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
package com.vada.caller.ui.theme
|
package com.vada.caller.ui.theme
|
||||||
|
|
||||||
import androidx.compose.runtime.staticCompositionLocalOf
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
|
|
||||||
val LocalDime = staticCompositionLocalOf { Dime() }
|
val LocalDime = staticCompositionLocalOf { Dime() }
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package com.vada.caller.ui.theme
|
package com.vada.caller.ui.theme
|
||||||
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
||||||
val MaterialTheme.dime: Dime
|
val MaterialTheme.dime: Dime
|
||||||
@Composable
|
@Composable
|
||||||
get() =LocalDime.current
|
get() =LocalDime.current
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
package com.fabirt.podcastapp.ui.theme
|
package com.fabirt.podcastapp.ui.theme
|
||||||
|
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Shapes
|
import androidx.compose.material3.Shapes
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
val Shapes = Shapes(
|
val Shapes = Shapes(
|
||||||
small = RoundedCornerShape(4.dp),
|
small = RoundedCornerShape(4.dp),
|
||||||
medium = RoundedCornerShape(12.dp),
|
medium = RoundedCornerShape(12.dp),
|
||||||
large = RoundedCornerShape(16.dp),
|
large = RoundedCornerShape(16.dp),
|
||||||
extraLarge = RoundedCornerShape(20.dp)
|
extraLarge = RoundedCornerShape(20.dp)
|
||||||
)
|
)
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
package com.approagency.drug.utils
|
package com.approagency.drug.utils
|
||||||
|
|
||||||
object Config {
|
object Config {
|
||||||
const val BASE_URL = "https://drug.approagency.ir/api/"
|
const val BASE_URL = "https://drug.approagency.ir/api/"
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package com.approagency.drug.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
suspend fun <T> retryWithBackoff(
|
||||||
|
maxRetries: Int = 3,
|
||||||
|
initialDelayMs: Long = 1000,
|
||||||
|
maxDelayMs: Long = 5000,
|
||||||
|
factor: Double = 2.0,
|
||||||
|
onRetry: ((attempt: Int, delayMs: Long) -> Unit)? = null,
|
||||||
|
block: suspend () -> T
|
||||||
|
): Result<T> {
|
||||||
|
var currentDelay = initialDelayMs
|
||||||
|
repeat(maxRetries - 1) { attempt ->
|
||||||
|
try {
|
||||||
|
return Result.success(block())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
onRetry?.invoke(attempt + 1, currentDelay)
|
||||||
|
delay(currentDelay)
|
||||||
|
currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelayMs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Last attempt
|
||||||
|
return try {
|
||||||
|
Result.success(block())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Result.failure(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
|
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
app:fontProviderAuthority="com.google.android.gms.fonts"
|
app:fontProviderAuthority="com.google.android.gms.fonts"
|
||||||
app:fontProviderPackage="com.google.android.gms"
|
app:fontProviderPackage="com.google.android.gms"
|
||||||
app:fontProviderQuery="Aldrich"
|
app:fontProviderQuery="Aldrich"
|
||||||
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
|
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
|
||||||
</font-family>
|
</font-family>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<array name="com_google_android_gms_fonts_certs">
|
<array name="com_google_android_gms_fonts_certs">
|
||||||
<item>@array/com_google_android_gms_fonts_certs_dev</item>
|
<item>@array/com_google_android_gms_fonts_certs_dev</item>
|
||||||
<item>@array/com_google_android_gms_fonts_certs_prod</item>
|
<item>@array/com_google_android_gms_fonts_certs_prod</item>
|
||||||
</array>
|
</array>
|
||||||
<string-array name="com_google_android_gms_fonts_certs_dev">
|
<string-array name="com_google_android_gms_fonts_certs_dev">
|
||||||
<item>
|
<item>
|
||||||
MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
|
MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
|
||||||
</item>
|
</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="com_google_android_gms_fonts_certs_prod">
|
<string-array name="com_google_android_gms_fonts_certs_prod">
|
||||||
<item>
|
<item>
|
||||||
MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
|
MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
|
||||||
</item>
|
</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<array name="preloaded_fonts" translatable="false">
|
<array name="preloaded_fonts" translatable="false">
|
||||||
<item>@font/aldrich</item>
|
<item>@font/aldrich</item>
|
||||||
</array>
|
</array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
+7
-1
@@ -21,4 +21,10 @@ kotlin.code.style=official
|
|||||||
# resources declared in the library itself and none from the library's dependencies,
|
# resources declared in the library itself and none from the library's dependencies,
|
||||||
# thereby reducing the size of the R class for that library
|
# thereby reducing the size of the R class for that library
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
kotlin.compiler.execution.strategy=in-process
|
kotlin.compiler.execution.strategy=in-process
|
||||||
|
# Disable downloading Gradle sources
|
||||||
|
org.gradle.caching=false
|
||||||
|
org.gradle.daemon=true
|
||||||
|
|
||||||
|
# Prevent Gradle from downloading sources
|
||||||
|
org.gradle.internal.launcher.welcomeMessageDisplayed=false
|
||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
#Thu Feb 26 19:18:21 IRST 2026
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://maven.myket.ir/gradle/distributions/gradle-8.13-bin.zip
|
distributionUrl=https\://maven.myket.ir/gradle/distributions/gradle-8.13-bin.zip
|
||||||
|
|||||||
Vendored
+94
-94
@@ -1,94 +1,94 @@
|
|||||||
@rem
|
@rem
|
||||||
@rem Copyright 2015 the original author or authors.
|
@rem Copyright 2015 the original author or authors.
|
||||||
@rem
|
@rem
|
||||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@rem you may not use this file except in compliance with the License.
|
@rem you may not use this file except in compliance with the License.
|
||||||
@rem You may obtain a copy of the License at
|
@rem You may obtain a copy of the License at
|
||||||
@rem
|
@rem
|
||||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
@rem
|
@rem
|
||||||
@rem Unless required by applicable law or agreed to in writing, software
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
@rem SPDX-License-Identifier: Apache-2.0
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@rem
|
@rem
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
|
|
||||||
@rem Set local scope for the variables with windows NT shell
|
@rem Set local scope for the variables with windows NT shell
|
||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
@rem This is normally unused
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo. 1>&2
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:findJavaFromJavaHome
|
:findJavaFromJavaHome
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo. 1>&2
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=
|
set CLASSPATH=
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
set EXIT_CODE=%ERRORLEVEL%
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
exit /b %EXIT_CODE%
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
:omega
|
:omega
|
||||||
|
|||||||
+8
-8
@@ -1,19 +1,19 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
maven { url = uri("https://maven.myket.ir") }
|
maven { url = uri("https://maven.myket.ir") }
|
||||||
google {
|
// google {
|
||||||
content {
|
// content {
|
||||||
includeGroupByRegex("com\\.android.*")
|
// includeGroupByRegex("com\\.android.*")
|
||||||
includeGroupByRegex("com\\.google.*")
|
// includeGroupByRegex("com\\.google.*")
|
||||||
includeGroupByRegex("androidx.*")
|
// includeGroupByRegex("androidx.*")
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// mavenCentral()
|
// mavenCentral()
|
||||||
// gradlePluginPortal()
|
// gradlePluginPortal()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
repositoriesMode.set(RepositoriesMode.PREFER_PROJECT)
|
||||||
repositories {
|
repositories {
|
||||||
//google()
|
//google()
|
||||||
//mavenCentral()
|
//mavenCentral()
|
||||||
|
|||||||
Reference in New Issue
Block a user