diff --git a/.idea/.gitignore b/.idea/.gitignore index 26d3352..eaf91e2 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -1,3 +1,3 @@ -# Default ignored files -/shelf/ -/workspace.xml +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/AndroidProjectSystem.xml b/.idea/AndroidProjectSystem.xml index 4a53bee..d58d49b 100644 --- a/.idea/AndroidProjectSystem.xml +++ b/.idea/AndroidProjectSystem.xml @@ -1,6 +1,6 @@ - - - - + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml index b86273d..8fabff5 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b268ef3..0d46093 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -1,10 +1,10 @@ - - - - - - - - + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 639c779..22d9498 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -1,19 +1,19 @@ - - - - - - + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml index f8051a6..48052b2 100644 --- a/.idea/migrations.xml +++ b/.idea/migrations.xml @@ -1,10 +1,10 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index b2c751a..f104487 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,7 @@ + - + diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml index 16660f1..5bd6771 100644 --- a/.idea/runConfigurations.xml +++ b/.idea/runConfigurations.xml @@ -1,17 +1,17 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7..9661ac7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file diff --git a/.kotlin/errors/errors-1772181807689.log b/.kotlin/errors/errors-1772181807689.log index 88b88b0..a155e2b 100644 --- a/.kotlin/errors/errors-1772181807689.log +++ b/.kotlin/errors/errors-1772181807689.log @@ -2,44 +2,44 @@ kotlin version: 2.0.21 error message: Failed connecting to the daemon in 4 retries error message: Daemon compilation failed: 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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) - at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) - 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) - 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.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:41) - 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$2.execute(DefaultBuildOperationRunner.java:67) - 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:60) - 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) - at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) - 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.access$700(DefaultConditionalExecutionQueue.java:127) - 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.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:132) - at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) - 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.FutureTask.run(Unknown Source) - at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) - 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$Worker.run(Unknown Source) - at java.base/java.lang.Thread.run(Unknown Source) +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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) + 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) + 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.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:41) + 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$2.execute(DefaultBuildOperationRunner.java:67) + 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:60) + 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) + at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) + 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.access$700(DefaultConditionalExecutionQueue.java:127) + 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.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:132) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) + 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.FutureTask.run(Unknown Source) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + 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$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) diff --git a/.kotlin/errors/errors-1772181967842.log b/.kotlin/errors/errors-1772181967842.log index 88b88b0..a155e2b 100644 --- a/.kotlin/errors/errors-1772181967842.log +++ b/.kotlin/errors/errors-1772181967842.log @@ -2,44 +2,44 @@ kotlin version: 2.0.21 error message: Failed connecting to the daemon in 4 retries error message: Daemon compilation failed: 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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) - at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) - 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) - 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.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:41) - 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$2.execute(DefaultBuildOperationRunner.java:67) - 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:60) - 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) - at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) - 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.access$700(DefaultConditionalExecutionQueue.java:127) - 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.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:132) - at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) - 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.FutureTask.run(Unknown Source) - at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) - 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$Worker.run(Unknown Source) - at java.base/java.lang.Thread.run(Unknown Source) +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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) + 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) + 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.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:41) + 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$2.execute(DefaultBuildOperationRunner.java:67) + 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:60) + 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) + at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) + 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.access$700(DefaultConditionalExecutionQueue.java:127) + 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.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:132) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) + 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.FutureTask.run(Unknown Source) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + 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$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) diff --git a/.kotlin/errors/errors-1772182373903.log b/.kotlin/errors/errors-1772182373903.log index 88b88b0..a155e2b 100644 --- a/.kotlin/errors/errors-1772182373903.log +++ b/.kotlin/errors/errors-1772182373903.log @@ -2,44 +2,44 @@ kotlin version: 2.0.21 error message: Failed connecting to the daemon in 4 retries error message: Daemon compilation failed: 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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) - at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) - 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) - 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.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:41) - 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$2.execute(DefaultBuildOperationRunner.java:67) - 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:60) - 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) - at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) - 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.access$700(DefaultConditionalExecutionQueue.java:127) - 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.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:132) - at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) - 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.FutureTask.run(Unknown Source) - at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) - 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$Worker.run(Unknown Source) - at java.base/java.lang.Thread.run(Unknown Source) +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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) + 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) + 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.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:41) + 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$2.execute(DefaultBuildOperationRunner.java:67) + 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:60) + 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) + at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) + 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.access$700(DefaultConditionalExecutionQueue.java:127) + 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.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:132) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) + 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.FutureTask.run(Unknown Source) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + 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$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) diff --git a/.kotlin/errors/errors-1772182457829.log b/.kotlin/errors/errors-1772182457829.log index 88b88b0..a155e2b 100644 --- a/.kotlin/errors/errors-1772182457829.log +++ b/.kotlin/errors/errors-1772182457829.log @@ -2,44 +2,44 @@ kotlin version: 2.0.21 error message: Failed connecting to the daemon in 4 retries error message: Daemon compilation failed: 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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) - at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) - 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) - 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.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:41) - 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$2.execute(DefaultBuildOperationRunner.java:67) - 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:60) - 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) - at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) - 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.access$700(DefaultConditionalExecutionQueue.java:127) - 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.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:132) - at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) - 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.FutureTask.run(Unknown Source) - at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) - 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$Worker.run(Unknown Source) - at java.base/java.lang.Thread.run(Unknown Source) +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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) + 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) + 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.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:41) + 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$2.execute(DefaultBuildOperationRunner.java:67) + 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:60) + 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) + at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) + 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.access$700(DefaultConditionalExecutionQueue.java:127) + 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.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:132) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) + 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.FutureTask.run(Unknown Source) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + 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$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) diff --git a/.kotlin/errors/errors-1772182895520.log b/.kotlin/errors/errors-1772182895520.log index 88b88b0..a155e2b 100644 --- a/.kotlin/errors/errors-1772182895520.log +++ b/.kotlin/errors/errors-1772182895520.log @@ -2,44 +2,44 @@ kotlin version: 2.0.21 error message: Failed connecting to the daemon in 4 retries error message: Daemon compilation failed: 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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) - at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) - 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) - 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.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:41) - 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$2.execute(DefaultBuildOperationRunner.java:67) - 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:60) - 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) - at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) - 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.access$700(DefaultConditionalExecutionQueue.java:127) - 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.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:132) - at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) - 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.FutureTask.run(Unknown Source) - at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) - 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$Worker.run(Unknown Source) - at java.base/java.lang.Thread.run(Unknown Source) +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.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159) + at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111) + 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.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) + 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.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:41) + 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$2.execute(DefaultBuildOperationRunner.java:67) + 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:60) + 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.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) + at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174) + 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.access$700(DefaultConditionalExecutionQueue.java:127) + 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.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:132) + at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) + 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.FutureTask.run(Unknown Source) + at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) + 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$Worker.run(Unknown Source) + at java.base/java.lang.Thread.run(Unknown Source) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cae693c..aec1799 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -31,11 +31,11 @@ android { } } compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "11" + jvmTarget = "17" } buildFeatures { compose = true diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3c8b9af..00e3d2f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,31 +1,31 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/DrugApp.kt b/app/src/main/java/com/approagency/drug/DrugApp.kt index f3c5812..55ad295 100644 --- a/app/src/main/java/com/approagency/drug/DrugApp.kt +++ b/app/src/main/java/com/approagency/drug/DrugApp.kt @@ -1,25 +1,25 @@ -package com.approagency.drug - -import android.app.Application -import com.approagency.drug.data.local.database.LabDatabase -import com.approagency.drug.di.appModule -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import org.koin.android.ext.koin.androidContext -import org.koin.core.context.startKoin - -class DrugApp : Application(){ - override fun onCreate() { - super.onCreate() - startKoin { - androidContext(this@DrugApp) - modules(appModule) - printLogger() - } - - // Preload database (optional, for faster access) - GlobalScope.launch { - LabDatabase.getInstance(this@DrugApp) - } - } +package com.approagency.drug + +import android.app.Application +import com.approagency.drug.data.local.database.LabDatabase +import com.approagency.drug.di.appModule +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.koin.android.ext.koin.androidContext +import org.koin.core.context.startKoin + +class DrugApp : Application(){ + override fun onCreate() { + super.onCreate() + startKoin { + androidContext(this@DrugApp) + modules(appModule) + printLogger() + } + + // Preload database (optional, for faster access) + GlobalScope.launch { + LabDatabase.getInstance(this@DrugApp) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/dto/DarmanModel.kt b/app/src/main/java/com/approagency/drug/data/dto/DarmanModel.kt index 449df41..5cd6ff2 100644 --- a/app/src/main/java/com/approagency/drug/data/dto/DarmanModel.kt +++ b/app/src/main/java/com/approagency/drug/data/dto/DarmanModel.kt @@ -1,13 +1,13 @@ -package com.approagency.drug.data.dto - -data class DarmanModel( - val success : String, - val message:String, - val data: List -) - -data class DarmanList( - val cod : Int, - val nam_fa:String, - val nam_en:String, +package com.approagency.drug.data.dto + +data class DarmanModel( + val success : String, + val message:String, + val data: List +) + +data class DarmanList( + val cod : Int, + val nam_fa:String, + val nam_en:String, ) \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/dto/DrugModels.kt b/app/src/main/java/com/approagency/drug/data/dto/DrugModels.kt index ed59b08..a0b2a4b 100644 --- a/app/src/main/java/com/approagency/drug/data/dto/DrugModels.kt +++ b/app/src/main/java/com/approagency/drug/data/dto/DrugModels.kt @@ -1,74 +1,74 @@ -package com.approagency.drug.data.dto - -data class DrugModels( - val success: Boolean, - val message: String?, - val data: DrugDetail?, - val meta: Meta? -) - -data class DrugDetail( - val cod: Int?, - val goroh_darmani_detail_cod: Int?, - val goroh_daroei_cod: Int?, - val goroh_farmakologic_cod: Int?, - val goroh_darmani_cod: Int?, - val nam_fa: String?, - val nam_en: String?, - val mavaredmasraf: String?, - val meghdarmasraf: String?, - val masrafdarhamelegi: String?, - val masrafdarshirdehi: String?, - val manemasraf: String?, - val avarez: String?, - val tadakhol: String?, - val mekanismtasir: String?, - val nokte: String?, - val hoshdar: String?, - val sharayetnegahdari: String?, - val ashkal_daroei: String?, - val created_at: String?, - val updated_at: String?, - val goroh_daroei: GorohDaroei?, - val goroh_darmani: GorohDarmani?, - val goroh_darmani_detail: GorohDarmaniDetail? -) - -data class GorohDaroei( - val cod: Int?, - val nam: String?, - val created_at: String?, - val updated_at: String? -) - -data class GorohDarmani( - val cod: Int?, - val nam_fa: String?, - val nam_en: String?, - val giyahi_ya_shimiyaei: String?, - val image: String?, - val created_at: String?, - val updated_at: String? -) - -data class GorohDarmaniDetail( - val cod: Int?, - val nam: String?, - val created_at: String?, - val updated_at: String? -) - -data class Meta( - val current_page: Int?, - val last_page: Int?, - val per_page: Int?, - val total: Int? -) - -// For list response compatibility -data class DrugListResponse( - val success: Boolean, - val message: String?, - val data: List = emptyList(), - val meta: Meta? +package com.approagency.drug.data.dto + +data class DrugModels( + val success: Boolean, + val message: String?, + val data: DrugDetail?, + val meta: Meta? +) + +data class DrugDetail( + val cod: Int?, + val goroh_darmani_detail_cod: Int?, + val goroh_daroei_cod: Int?, + val goroh_farmakologic_cod: Int?, + val goroh_darmani_cod: Int?, + val nam_fa: String?, + val nam_en: String?, + val mavaredmasraf: String?, + val meghdarmasraf: String?, + val masrafdarhamelegi: String?, + val masrafdarshirdehi: String?, + val manemasraf: String?, + val avarez: String?, + val tadakhol: String?, + val mekanismtasir: String?, + val nokte: String?, + val hoshdar: String?, + val sharayetnegahdari: String?, + val ashkal_daroei: String?, + val created_at: String?, + val updated_at: String?, + val goroh_daroei: GorohDaroei?, + val goroh_darmani: GorohDarmani?, + val goroh_darmani_detail: GorohDarmaniDetail? +) + +data class GorohDaroei( + val cod: Int?, + val nam: String?, + val created_at: String?, + val updated_at: String? +) + +data class GorohDarmani( + val cod: Int?, + val nam_fa: String?, + val nam_en: String?, + val giyahi_ya_shimiyaei: String?, + val image: String?, + val created_at: String?, + val updated_at: String? +) + +data class GorohDarmaniDetail( + val cod: Int?, + val nam: String?, + val created_at: String?, + val updated_at: String? +) + +data class Meta( + val current_page: Int?, + val last_page: Int?, + val per_page: Int?, + val total: Int? +) + +// For list response compatibility +data class DrugListResponse( + val success: Boolean, + val message: String?, + val data: List = emptyList(), + val meta: Meta? ) \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/local/dao/TestGroupDao.kt b/app/src/main/java/com/approagency/drug/data/local/dao/TestGroupDao.kt index a39bdd7..27668c1 100644 --- a/app/src/main/java/com/approagency/drug/data/local/dao/TestGroupDao.kt +++ b/app/src/main/java/com/approagency/drug/data/local/dao/TestGroupDao.kt @@ -1,32 +1,46 @@ -package com.approagency.drug.data.local.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import com.approagency.drug.data.local.entities.TestGroupEntity -import kotlinx.coroutines.flow.Flow - -@Dao -interface TestGroupDao { - @Query("SELECT * FROM testGroup ORDER BY Id") - fun getAllGroups(): Flow> - - @Query("SELECT * FROM testGroup WHERE Id = :groupId") - suspend fun getGroupById(groupId: Int): TestGroupEntity? - - @Query("SELECT * FROM testGroup WHERE Isparent = '1'") - fun getParentGroups(): Flow> - - @Query("SELECT * FROM testGroup WHERE Isparent = '0'") - fun getChildGroups(): Flow> - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertGroup(group: TestGroupEntity) - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAllGroups(groups: List) - - @Query("DELETE FROM testGroup") - suspend fun deleteAllGroups() +package com.approagency.drug.data.local.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.approagency.drug.data.local.entities.TestGroupEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface TestGroupDao { + @Query("SELECT * FROM testGroup ORDER BY Id") + fun getAllGroups(): Flow> + + @Query("SELECT * FROM testGroup WHERE Id = :groupId") + suspend fun getGroupById(groupId: Int): TestGroupEntity? + + @Query("SELECT * FROM testGroup WHERE Isparent = '1'") + fun getParentGroups(): Flow> + + @Query("SELECT * FROM testGroup WHERE Isparent = '0'") + fun getChildGroups(): Flow> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertGroup(group: TestGroupEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAllGroups(groups: List) + + @Query("DELETE FROM testGroup") + 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> } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/local/dao/TestItemDao.kt b/app/src/main/java/com/approagency/drug/data/local/dao/TestItemDao.kt index 5740cbd..4ff2a1c 100644 --- a/app/src/main/java/com/approagency/drug/data/local/dao/TestItemDao.kt +++ b/app/src/main/java/com/approagency/drug/data/local/dao/TestItemDao.kt @@ -1,36 +1,46 @@ -package com.approagency.drug.data.local.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import com.approagency.drug.data.local.entities.TestItemEntity -import kotlinx.coroutines.flow.Flow - -@Dao -interface TestItemDao { - @Query("SELECT * FROM testItem ORDER BY Id") - fun getAllItems(): Flow> - - @Query("SELECT * FROM testItem WHERE Group_Id = :groupId ORDER BY Id") - fun getItemsByGroupId(groupId: Int): Flow> - - @Query("SELECT * FROM testItem WHERE Id = :itemId") - suspend fun getItemById(itemId: Int): TestItemEntity? - - @Query(""" - SELECT ti.* FROM testItem ti - INNER JOIN testGroup tg ON ti.Group_Id = tg.Id - WHERE tg.Isparent = '0' AND ti.Title LIKE '%' || :searchQuery || '%' - """) - fun searchTestItems(searchQuery: String): Flow> - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertItem(item: TestItemEntity) - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAllItems(items: List) - - @Query("DELETE FROM testItem") - suspend fun deleteAllItems() +package com.approagency.drug.data.local.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.approagency.drug.data.local.entities.TestItemEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface TestItemDao { + @Query("SELECT * FROM testItem ORDER BY Id") + fun getAllItems(): Flow> + + @Query("SELECT * FROM testItem WHERE Group_Id = :groupId ORDER BY Id") + fun getItemsByGroupId(groupId: Int): Flow> + + @Query("SELECT * FROM testItem WHERE Id = :itemId") + suspend fun getItemById(itemId: Int): TestItemEntity? + + @Query(""" + SELECT ti.* FROM testItem ti + LEFT JOIN testGroup tg ON ti.Group_Id = tg.Id + WHERE ti.Title LIKE '%' || :searchQuery || '%' + OR ti.Normal_Value LIKE '%' || :searchQuery || '%' + OR ti.Detail LIKE '%' || :searchQuery || '%' + OR tg.Fname LIKE '%' || :searchQuery || '%' + OR tg.Ename LIKE '%' || :searchQuery || '%' + ORDER BY + CASE + WHEN ti.Title LIKE '%' || :searchQuery || '%' THEN 1 + WHEN tg.Fname LIKE '%' || :searchQuery || '%' THEN 2 + ELSE 3 + END + """) + fun searchTestItems(searchQuery: String): Flow> + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertItem(item: TestItemEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertAllItems(items: List) + + @Query("DELETE FROM testItem") + suspend fun deleteAllItems() } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/local/database/LabDatabase.kt b/app/src/main/java/com/approagency/drug/data/local/database/LabDatabase.kt index f7f30df..2a35fcd 100644 --- a/app/src/main/java/com/approagency/drug/data/local/database/LabDatabase.kt +++ b/app/src/main/java/com/approagency/drug/data/local/database/LabDatabase.kt @@ -1,43 +1,43 @@ -package com.approagency.drug.data.local.database - -import android.content.Context -import androidx.room.Database -import androidx.room.Room -import androidx.room.RoomDatabase -import com.approagency.drug.data.local.dao.TestGroupDao -import com.approagency.drug.data.local.dao.TestItemDao -import com.approagency.drug.data.local.entities.TestGroupEntity -import com.approagency.drug.data.local.entities.TestItemEntity - -@Database( - entities = [ - TestGroupEntity::class, - TestItemEntity::class - ], - version = 1, - exportSchema = false -) -abstract class LabDatabase : RoomDatabase() { - abstract fun testGroupDao(): TestGroupDao - abstract fun testItemDao(): TestItemDao - - companion object { - @Volatile - private var INSTANCE: LabDatabase? = null - - fun getInstance(context: Context): LabDatabase { - return INSTANCE ?: synchronized(this) { - val instance = Room.databaseBuilder( - context.applicationContext, - LabDatabase::class.java, - "lab_tests.db" // This will use your existing database - ) - .createFromAsset("lab_tests.db") // IMPORTANT: Copy from assets - .fallbackToDestructiveMigration() // For development only - .build() - INSTANCE = instance - instance - } - } - } +package com.approagency.drug.data.local.database + +import android.content.Context +import androidx.room.Database +import androidx.room.Room +import androidx.room.RoomDatabase +import com.approagency.drug.data.local.dao.TestGroupDao +import com.approagency.drug.data.local.dao.TestItemDao +import com.approagency.drug.data.local.entities.TestGroupEntity +import com.approagency.drug.data.local.entities.TestItemEntity + +@Database( + entities = [ + TestGroupEntity::class, + TestItemEntity::class + ], + version = 1, + exportSchema = false +) +abstract class LabDatabase : RoomDatabase() { + abstract fun testGroupDao(): TestGroupDao + abstract fun testItemDao(): TestItemDao + + companion object { + @Volatile + private var INSTANCE: LabDatabase? = null + + fun getInstance(context: Context): LabDatabase { + return INSTANCE ?: synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + LabDatabase::class.java, + "lab_tests.db" // This will use your existing database + ) + .createFromAsset("lab_tests.db") // IMPORTANT: Copy from assets + .fallbackToDestructiveMigration() // For development only + .build() + INSTANCE = instance + instance + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/local/entities/TestGroupEntity.kt b/app/src/main/java/com/approagency/drug/data/local/entities/TestGroupEntity.kt index a256e47..e69c90b 100644 --- a/app/src/main/java/com/approagency/drug/data/local/entities/TestGroupEntity.kt +++ b/app/src/main/java/com/approagency/drug/data/local/entities/TestGroupEntity.kt @@ -1,24 +1,24 @@ -package com.approagency.drug.data.local.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity(tableName = "testGroup") -data class TestGroupEntity( - @PrimaryKey - @ColumnInfo(name = "Id") - val id: Int, - - @ColumnInfo(name = "Ename") - val ename: String?, - - @ColumnInfo(name = "Fname") - val fname: String?, - - @ColumnInfo(name = "Detail") - val detail: String?, - - @ColumnInfo(name = "Isparent") - val isParent: String? +package com.approagency.drug.data.local.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "testGroup") +data class TestGroupEntity( + @PrimaryKey + @ColumnInfo(name = "Id") + val id: Int, + + @ColumnInfo(name = "Ename") + val ename: String?, + + @ColumnInfo(name = "Fname") + val fname: String?, + + @ColumnInfo(name = "Detail") + val detail: String?, + + @ColumnInfo(name = "Isparent") + val isParent: String? ) \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/local/entities/TestItemEntity.kt b/app/src/main/java/com/approagency/drug/data/local/entities/TestItemEntity.kt index e4d814f..c6490b0 100644 --- a/app/src/main/java/com/approagency/drug/data/local/entities/TestItemEntity.kt +++ b/app/src/main/java/com/approagency/drug/data/local/entities/TestItemEntity.kt @@ -1,37 +1,37 @@ -package com.approagency.drug.data.local.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.Index -import androidx.room.PrimaryKey - -@Entity( - tableName = "testItem", -// foreignKeys = [ -// ForeignKey( -// entity = TestGroupEntity::class, -// parentColumns = ["Id"], -// childColumns = ["Group_Id"], -// onDelete = ForeignKey.CASCADE -// ) -// ], - indices = [Index(value = ["Group_Id"])] -) -data class TestItemEntity( - @PrimaryKey - @ColumnInfo(name = "Id") - val id: Int, - - @ColumnInfo(name = "Group_Id") - val groupId: Int, - - @ColumnInfo(name = "Title") - val title: String?, - - @ColumnInfo(name = "Normal_Value") - val normalValue: String?, - - @ColumnInfo(name = "Detail") - val detail: String? +package com.approagency.drug.data.local.entities + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.Index +import androidx.room.PrimaryKey + +@Entity( + tableName = "testItem", +// foreignKeys = [ +// ForeignKey( +// entity = TestGroupEntity::class, +// parentColumns = ["Id"], +// childColumns = ["Group_Id"], +// onDelete = ForeignKey.CASCADE +// ) +// ], + indices = [Index(value = ["Group_Id"])] +) +data class TestItemEntity( + @PrimaryKey + @ColumnInfo(name = "Id") + val id: Int, + + @ColumnInfo(name = "Group_Id") + val groupId: Int, + + @ColumnInfo(name = "Title") + val title: String?, + + @ColumnInfo(name = "Normal_Value") + val normalValue: String?, + + @ColumnInfo(name = "Detail") + val detail: String? ) \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/remote/DrugApiService.kt b/app/src/main/java/com/approagency/drug/data/remote/DrugApiService.kt index 4ff099c..0aff4cf 100644 --- a/app/src/main/java/com/approagency/drug/data/remote/DrugApiService.kt +++ b/app/src/main/java/com/approagency/drug/data/remote/DrugApiService.kt @@ -1,29 +1,29 @@ -package com.approagency.drug.data.remote - -import com.approagency.drug.data.dto.DarmanModel -import com.approagency.drug.data.dto.DrugListResponse -import com.approagency.drug.data.dto.DrugModels -import retrofit2.http.GET -import retrofit2.http.Path -import retrofit2.http.Query - -interface DrugApiService { - @GET("drugs/search") - suspend fun getDrugs( - @Query("q")query: String?, - @Query("per_page")perPage:Int? = 20, - @Query("with_relations")withRelations: Boolean? =true, - @Query("goroh_daroei_cod")drugGroup:Int?, - @Query("goroh_darmani_cod")healGroup:Int?, - ): DrugListResponse - - - @GET("drugs/{cod}") - suspend fun getDrugDetail( - @Path("cod") cod: Int - ): DrugModels - - - @GET("drugs/goroh-darmani") - suspend fun getGorohDaroei(): DarmanModel +package com.approagency.drug.data.remote + +import com.approagency.drug.data.dto.DarmanModel +import com.approagency.drug.data.dto.DrugListResponse +import com.approagency.drug.data.dto.DrugModels +import retrofit2.http.GET +import retrofit2.http.Path +import retrofit2.http.Query + +interface DrugApiService { + @GET("drugs/search") + suspend fun getDrugs( + @Query("q")query: String?, + @Query("per_page")perPage:Int? = 20, + @Query("with_relations")withRelations: Boolean? =true, + @Query("goroh_daroei_cod")drugGroup:Int?, + @Query("goroh_darmani_cod")healGroup:Int?, + ): DrugListResponse + + + @GET("drugs/{cod}") + suspend fun getDrugDetail( + @Path("cod") cod: Int + ): DrugModels + + + @GET("drugs/goroh-darmani") + suspend fun getGorohDaroei(): DarmanModel } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/repository/DrugRepositoryImpl.kt b/app/src/main/java/com/approagency/drug/data/repository/DrugRepositoryImpl.kt index 8bbb131..1c67c77 100644 --- a/app/src/main/java/com/approagency/drug/data/repository/DrugRepositoryImpl.kt +++ b/app/src/main/java/com/approagency/drug/data/repository/DrugRepositoryImpl.kt @@ -1,39 +1,39 @@ -package com.approagency.drug.data.repository - -import com.approagency.drug.data.dto.DarmanModel -import com.approagency.drug.data.dto.DrugListResponse -import com.approagency.drug.data.dto.DrugModels -import com.approagency.drug.data.remote.DrugApiService -import com.approagency.drug.domain.model.DrugSearchParams -import com.approagency.drug.domain.repository.DrugRepository - -class DrugRepositoryImpl( - private val apiService: DrugApiService -): DrugRepository { - override suspend fun searchDrug(params: DrugSearchParams): Result { - return try { - val response = apiService.getDrugs(query = params.query , perPage = params.perPage , withRelations = params.withRelations , healGroup = params.healGroup , drugGroup = params.drugGroup ) - return Result.success(response) - }catch (e: Exception){ - Result.failure(e) - } - } - - override suspend fun drugDetail(cod: Int): Result { - return try { - val response= apiService.getDrugDetail(cod = cod) - return Result.success(response) - }catch (e: Exception){ - Result.failure(e) - } - } - - override suspend fun getGorohDaroei(): Result { - return try { - val response = apiService.getGorohDaroei() - return Result.success(response) - }catch (e: Exception){ - Result.failure(e) - } - } +package com.approagency.drug.data.repository + +import com.approagency.drug.data.dto.DarmanModel +import com.approagency.drug.data.dto.DrugListResponse +import com.approagency.drug.data.dto.DrugModels +import com.approagency.drug.data.remote.DrugApiService +import com.approagency.drug.domain.model.DrugSearchParams +import com.approagency.drug.domain.repository.DrugRepository + +class DrugRepositoryImpl( + private val apiService: DrugApiService +): DrugRepository { + override suspend fun searchDrug(params: DrugSearchParams): Result { + return try { + val response = apiService.getDrugs(query = params.query , perPage = params.perPage , withRelations = params.withRelations , healGroup = params.healGroup , drugGroup = params.drugGroup ) + return Result.success(response) + }catch (e: Exception){ + Result.failure(e) + } + } + + override suspend fun drugDetail(cod: Int): Result { + return try { + val response= apiService.getDrugDetail(cod = cod) + return Result.success(response) + }catch (e: Exception){ + Result.failure(e) + } + } + + override suspend fun getGorohDaroei(): Result { + return try { + val response = apiService.getGorohDaroei() + return Result.success(response) + }catch (e: Exception){ + Result.failure(e) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/data/repository/LabRepositoryImpl.kt b/app/src/main/java/com/approagency/drug/data/repository/LabRepositoryImpl.kt index db5fd32..ad775d1 100644 --- a/app/src/main/java/com/approagency/drug/data/repository/LabRepositoryImpl.kt +++ b/app/src/main/java/com/approagency/drug/data/repository/LabRepositoryImpl.kt @@ -1,24 +1,35 @@ -package com.approagency.drug.data.repository - -import com.approagency.drug.data.local.dao.TestGroupDao -import com.approagency.drug.data.local.dao.TestItemDao -import com.approagency.drug.data.local.entities.TestGroupEntity -import com.approagency.drug.data.local.entities.TestItemEntity -import kotlinx.coroutines.flow.Flow - -class LabRepositoryImpl ( - private val testGroupDao: TestGroupDao, - private val testItemDao: TestItemDao -) { - // Test Group Operations - fun getAllGroups(): Flow> = testGroupDao.getAllGroups() - fun getParentGroups(): Flow> = testGroupDao.getParentGroups() - fun getChildGroups(): Flow> = testGroupDao.getChildGroups() - suspend fun getGroupById(groupId: Int): TestGroupEntity? = testGroupDao.getGroupById(groupId) - - // Test Item Operations - fun getAllItems(): Flow> = testItemDao.getAllItems() - fun getItemsByGroupId(groupId: Int): Flow> = testItemDao.getItemsByGroupId(groupId) - suspend fun getItemById(itemId: Int): TestItemEntity? = testItemDao.getItemById(itemId) - fun searchTestItems(query: String): Flow> = testItemDao.searchTestItems(query) +package com.approagency.drug.data.repository + +import com.approagency.drug.data.local.dao.TestGroupDao +import com.approagency.drug.data.local.dao.TestItemDao +import com.approagency.drug.data.local.entities.TestGroupEntity +import com.approagency.drug.data.local.entities.TestItemEntity +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +class LabRepositoryImpl ( + private val testGroupDao: TestGroupDao, + private val testItemDao: TestItemDao +) { + // Test Group Operations + fun getAllGroups(): Flow> = testGroupDao.getAllGroups() + fun getParentGroups(): Flow> = testGroupDao.getParentGroups() + fun getChildGroups(): Flow> = testGroupDao.getChildGroups() + suspend fun getGroupById(groupId: Int): TestGroupEntity? = testGroupDao.getGroupById(groupId) + + // Test Item Operations + fun getAllItems(): Flow> = testItemDao.getAllItems() + fun getItemsByGroupId(groupId: Int): Flow> = testItemDao.getItemsByGroupId(groupId) + suspend fun getItemById(itemId: Int): TestItemEntity? = testItemDao.getItemById(itemId) + fun searchTestItems(query: String): Flow> = testItemDao.searchTestItems(query) + + + fun searchGroupsAndItems(searchQuery: String): Flow, List>> { + return combine( + testGroupDao.searchGroups(searchQuery), + testItemDao.searchTestItems(searchQuery) + ) { groups, items -> + Pair(groups, items) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/di/AppModule.kt b/app/src/main/java/com/approagency/drug/di/AppModule.kt index e4fb0bf..0f3eb4f 100644 --- a/app/src/main/java/com/approagency/drug/di/AppModule.kt +++ b/app/src/main/java/com/approagency/drug/di/AppModule.kt @@ -1,79 +1,82 @@ -package com.approagency.drug.di - - -import android.app.Application -import com.approagency.drug.data.local.database.LabDatabase -import com.approagency.drug.data.remote.DrugApiService -import com.approagency.drug.data.repository.DrugRepositoryImpl -import com.approagency.drug.data.repository.LabRepositoryImpl -import com.approagency.drug.domain.repository.DrugRepository -import com.approagency.drug.domain.usecase.GetDarmanUseCase -import com.approagency.drug.domain.usecase.GetDrugDetailUseCase -import com.approagency.drug.domain.usecase.GetDrugSearchUseCase -import com.approagency.drug.domain.usecase.GetTestGroupUseCase -import com.approagency.drug.domain.usecase.GetTestItemByGroupId -import com.approagency.drug.presentation.viewModel.HomeViewModel -import com.approagency.drug.presentation.viewModel.LabViewModel -import com.approagency.drug.utils.Config -import org.koin.android.ext.koin.androidContext -import org.koin.core.module.dsl.viewModel -import org.koin.dsl.module -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory - -val appModule= module { - - single { LabDatabase.getInstance(androidContext()) } - - single { get().testGroupDao() } - single { get().testItemDao() } - - single { - Retrofit.Builder() - .baseUrl(Config.BASE_URL) - .addConverterFactory(GsonConverterFactory.create()) - .build() - } - single { - get().create(DrugApiService::class.java) - } - - //repo - single { - DrugRepositoryImpl(get()) - } - - - - //UseCase - single { - GetDrugSearchUseCase(get()) - - } - - single{ - GetDrugDetailUseCase(get()) - } - - single { - GetDarmanUseCase(get()) - } - - single { - GetTestGroupUseCase(get()) - } - - single { - GetTestItemByGroupId(get()) - } - - single { LabRepositoryImpl(get(), get()) } - //view model - viewModel { - HomeViewModel(get() , get() , get()) - } - - viewModel { - LabViewModel(get() , get()) - } +package com.approagency.drug.di + + +import android.app.Application +import com.approagency.drug.data.local.database.LabDatabase +import com.approagency.drug.data.remote.DrugApiService +import com.approagency.drug.data.repository.DrugRepositoryImpl +import com.approagency.drug.data.repository.LabRepositoryImpl +import com.approagency.drug.domain.repository.DrugRepository +import com.approagency.drug.domain.usecase.GetDarmanUseCase +import com.approagency.drug.domain.usecase.GetDrugDetailUseCase +import com.approagency.drug.domain.usecase.GetDrugSearchUseCase +import com.approagency.drug.domain.usecase.GetTestGroupUseCase +import com.approagency.drug.domain.usecase.GetTestItemByGroupId +import com.approagency.drug.domain.usecase.SearchTestsUseCase +import com.approagency.drug.presentation.viewModel.HomeViewModel +import com.approagency.drug.presentation.viewModel.LabViewModel +import com.approagency.drug.utils.Config +import org.koin.android.ext.koin.androidContext +import org.koin.core.module.dsl.viewModel +import org.koin.dsl.module +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +val appModule= module { + + single { LabDatabase.getInstance(androidContext()) } + + single { get().testGroupDao() } + single { get().testItemDao() } + + single { + Retrofit.Builder() + .baseUrl(Config.BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .build() + } + single { + get().create(DrugApiService::class.java) + } + + //repo + single { + DrugRepositoryImpl(get()) + } + + + + //UseCase + single { + GetDrugSearchUseCase(get()) + + } + + single{ + GetDrugDetailUseCase(get()) + } + + single { + GetDarmanUseCase(get()) + } + + single { + GetTestGroupUseCase(get()) + } + + single { + GetTestItemByGroupId(get()) + } + + single { LabRepositoryImpl(get(), get()) } + + single { SearchTestsUseCase(get()) } + //view model + viewModel { + HomeViewModel(get() , get() , get()) + } + + viewModel { + LabViewModel(get() , get() , get()) + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/model/DrugSearchParams.kt b/app/src/main/java/com/approagency/drug/domain/model/DrugSearchParams.kt index bef129d..49b9762 100644 --- a/app/src/main/java/com/approagency/drug/domain/model/DrugSearchParams.kt +++ b/app/src/main/java/com/approagency/drug/domain/model/DrugSearchParams.kt @@ -1,9 +1,9 @@ -package com.approagency.drug.domain.model - -data class DrugSearchParams( - val query: String? = null, - val perPage: Int = 20, - val withRelations: Boolean = true, - val drugGroup: Int? = null, - val healGroup: Int? = null, -) +package com.approagency.drug.domain.model + +data class DrugSearchParams( + val query: String? = null, + val perPage: Int = 20, + val withRelations: Boolean = true, + val drugGroup: Int? = null, + val healGroup: Int? = null, +) diff --git a/app/src/main/java/com/approagency/drug/domain/model/TestGroup.kt b/app/src/main/java/com/approagency/drug/domain/model/TestGroup.kt index 62f23f8..94d0362 100644 --- a/app/src/main/java/com/approagency/drug/domain/model/TestGroup.kt +++ b/app/src/main/java/com/approagency/drug/domain/model/TestGroup.kt @@ -1,11 +1,22 @@ -package com.approagency.drug.domain.model - -data class TestGroup( - val id: Int, - val ename: String?, - val fname: String?, - val detail: String?, - val isParent: Boolean -) - - +package com.approagency.drug.domain.model + +import com.approagency.drug.data.local.entities.TestGroupEntity + +data class TestGroup( + val id: Int, + val ename: String?, + 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 + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/model/TestItem.kt b/app/src/main/java/com/approagency/drug/domain/model/TestItem.kt index db680fa..5033582 100644 --- a/app/src/main/java/com/approagency/drug/domain/model/TestItem.kt +++ b/app/src/main/java/com/approagency/drug/domain/model/TestItem.kt @@ -1,9 +1,20 @@ -package com.approagency.drug.domain.model - -data class TestItem( - val id: Int, - val groupId: Int, - val title: String?, - val normalValue: String?, - val detail: String? -) +package com.approagency.drug.domain.model + +import com.approagency.drug.data.local.entities.TestItemEntity + +data class TestItem( + val id: Int, + val groupId: Int, + 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 + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/repository/DrugRepository.kt b/app/src/main/java/com/approagency/drug/domain/repository/DrugRepository.kt index ac7a2a5..d0790b0 100644 --- a/app/src/main/java/com/approagency/drug/domain/repository/DrugRepository.kt +++ b/app/src/main/java/com/approagency/drug/domain/repository/DrugRepository.kt @@ -1,12 +1,12 @@ -package com.approagency.drug.domain.repository - -import com.approagency.drug.data.dto.DarmanModel -import com.approagency.drug.data.dto.DrugListResponse -import com.approagency.drug.data.dto.DrugModels -import com.approagency.drug.domain.model.DrugSearchParams - -interface DrugRepository { - suspend fun searchDrug(params: DrugSearchParams): Result - suspend fun drugDetail(cod:Int): Result - suspend fun getGorohDaroei(): Result +package com.approagency.drug.domain.repository + +import com.approagency.drug.data.dto.DarmanModel +import com.approagency.drug.data.dto.DrugListResponse +import com.approagency.drug.data.dto.DrugModels +import com.approagency.drug.domain.model.DrugSearchParams + +interface DrugRepository { + suspend fun searchDrug(params: DrugSearchParams): Result + suspend fun drugDetail(cod:Int): Result + suspend fun getGorohDaroei(): Result } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/usecase/GetDarmanUseCase.kt b/app/src/main/java/com/approagency/drug/domain/usecase/GetDarmanUseCase.kt index f36aa61..9302daa 100644 --- a/app/src/main/java/com/approagency/drug/domain/usecase/GetDarmanUseCase.kt +++ b/app/src/main/java/com/approagency/drug/domain/usecase/GetDarmanUseCase.kt @@ -1,12 +1,12 @@ -package com.approagency.drug.domain.usecase - -import com.approagency.drug.data.dto.DarmanModel -import com.approagency.drug.domain.repository.DrugRepository - -class GetDarmanUseCase ( - private val repository: DrugRepository -){ - suspend operator fun invoke (): Result{ - return repository.getGorohDaroei() - } +package com.approagency.drug.domain.usecase + +import com.approagency.drug.data.dto.DarmanModel +import com.approagency.drug.domain.repository.DrugRepository + +class GetDarmanUseCase ( + private val repository: DrugRepository +){ + suspend operator fun invoke (): Result{ + return repository.getGorohDaroei() + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/usecase/GetDrugDetailUseCase.kt b/app/src/main/java/com/approagency/drug/domain/usecase/GetDrugDetailUseCase.kt index 38b5201..b137852 100644 --- a/app/src/main/java/com/approagency/drug/domain/usecase/GetDrugDetailUseCase.kt +++ b/app/src/main/java/com/approagency/drug/domain/usecase/GetDrugDetailUseCase.kt @@ -1,12 +1,12 @@ -package com.approagency.drug.domain.usecase - -import com.approagency.drug.data.dto.DrugModels -import com.approagency.drug.domain.repository.DrugRepository - -class GetDrugDetailUseCase - ( - private val repository: DrugRepository){ - suspend operator fun invoke(cod:Int): Result{ - return repository.drugDetail(cod) - } +package com.approagency.drug.domain.usecase + +import com.approagency.drug.data.dto.DrugModels +import com.approagency.drug.domain.repository.DrugRepository + +class GetDrugDetailUseCase + ( + private val repository: DrugRepository){ + suspend operator fun invoke(cod:Int): Result{ + return repository.drugDetail(cod) + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/usecase/GetDrugSearchUseCase.kt b/app/src/main/java/com/approagency/drug/domain/usecase/GetDrugSearchUseCase.kt index 501efb8..0b5fe84 100644 --- a/app/src/main/java/com/approagency/drug/domain/usecase/GetDrugSearchUseCase.kt +++ b/app/src/main/java/com/approagency/drug/domain/usecase/GetDrugSearchUseCase.kt @@ -1,13 +1,13 @@ -package com.approagency.drug.domain.usecase - -import com.approagency.drug.data.dto.DrugListResponse -import com.approagency.drug.domain.model.DrugSearchParams -import com.approagency.drug.domain.repository.DrugRepository - -class GetDrugSearchUseCase ( - private val repository: DrugRepository -){ - suspend operator fun invoke(params: DrugSearchParams): Result { - return repository.searchDrug(params) - } +package com.approagency.drug.domain.usecase + +import com.approagency.drug.data.dto.DrugListResponse +import com.approagency.drug.domain.model.DrugSearchParams +import com.approagency.drug.domain.repository.DrugRepository + +class GetDrugSearchUseCase ( + private val repository: DrugRepository +){ + suspend operator fun invoke(params: DrugSearchParams): Result { + return repository.searchDrug(params) + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/usecase/GetTestGroupUseCase.kt b/app/src/main/java/com/approagency/drug/domain/usecase/GetTestGroupUseCase.kt index 8944e98..7400d5b 100644 --- a/app/src/main/java/com/approagency/drug/domain/usecase/GetTestGroupUseCase.kt +++ b/app/src/main/java/com/approagency/drug/domain/usecase/GetTestGroupUseCase.kt @@ -1,24 +1,24 @@ -package com.approagency.drug.domain.usecase - -import com.approagency.drug.data.repository.LabRepositoryImpl -import com.approagency.drug.domain.model.TestGroup -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map - -class GetTestGroupUseCase ( - private val repository: LabRepositoryImpl -) { - operator fun invoke(): Flow> { - return repository.getAllGroups().map { entities -> - entities.map { entity -> - TestGroup( - id = entity.id, - ename = entity.ename, - fname = entity.fname, - detail = entity.detail, - isParent = entity.isParent == "1" - ) - } - } - } +package com.approagency.drug.domain.usecase + +import com.approagency.drug.data.repository.LabRepositoryImpl +import com.approagency.drug.domain.model.TestGroup +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class GetTestGroupUseCase ( + private val repository: LabRepositoryImpl +) { + operator fun invoke(): Flow> { + return repository.getAllGroups().map { entities -> + entities.map { entity -> + TestGroup( + id = entity.id, + ename = entity.ename, + fname = entity.fname, + detail = entity.detail, + isParent = entity.isParent + ) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/usecase/GetTestItemByGroupId.kt b/app/src/main/java/com/approagency/drug/domain/usecase/GetTestItemByGroupId.kt index f2b459e..5172e42 100644 --- a/app/src/main/java/com/approagency/drug/domain/usecase/GetTestItemByGroupId.kt +++ b/app/src/main/java/com/approagency/drug/domain/usecase/GetTestItemByGroupId.kt @@ -1,26 +1,26 @@ -package com.approagency.drug.domain.usecase - -import com.approagency.drug.data.local.entities.TestItemEntity -import com.approagency.drug.data.repository.LabRepositoryImpl -import com.approagency.drug.domain.model.TestItem -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map - -class GetTestItemByGroupId ( - private val repository: LabRepositoryImpl -) { - operator fun invoke(id:Int): Flow> { - return repository.getItemsByGroupId(groupId = id).map { - entities -> entities.map { - entity -> - TestItem( - id = entity.id, - groupId = entity.groupId, - title = entity.title, - normalValue = entity.normalValue, - detail = entity.detail, - ) - } - } - } +package com.approagency.drug.domain.usecase + +import com.approagency.drug.data.local.entities.TestItemEntity +import com.approagency.drug.data.repository.LabRepositoryImpl +import com.approagency.drug.domain.model.TestItem +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class GetTestItemByGroupId ( + private val repository: LabRepositoryImpl +) { + operator fun invoke(id:Int): Flow> { + return repository.getItemsByGroupId(groupId = id).map { + entities -> entities.map { + entity -> + TestItem( + id = entity.id, + groupId = entity.groupId, + title = entity.title, + normalValue = entity.normalValue, + detail = entity.detail, + ) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/domain/usecase/SearchTestsUseCase.kt b/app/src/main/java/com/approagency/drug/domain/usecase/SearchTestsUseCase.kt new file mode 100644 index 0000000..410feb8 --- /dev/null +++ b/app/src/main/java/com/approagency/drug/domain/usecase/SearchTestsUseCase.kt @@ -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, List>> { + return repository.searchGroupsAndItems(query).map { (groups, items) -> + Pair( + groups.map { it.toTestGroup() }, + items.map { it.toTestItem() } + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/navigation/Graph.kt b/app/src/main/java/com/approagency/drug/navigation/Graph.kt index 6c62357..97c2b7b 100644 --- a/app/src/main/java/com/approagency/drug/navigation/Graph.kt +++ b/app/src/main/java/com/approagency/drug/navigation/Graph.kt @@ -1,24 +1,24 @@ -package com.approagency.drug.navigation - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavController -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import com.approagency.drug.presentation.screens.HomeScreen -import androidx.navigation.compose.composable -import com.approagency.drug.presentation.screens.RootScreen - -@Composable -fun AppNavGraph(navController: NavHostController, modifier: Modifier = Modifier) { - NavHost( - navController = navController , - startDestination = NavRoutes.Home.route - ){ - composable(NavRoutes.Home.route) { - RootScreen( - navHostController = navController , modifier = Modifier - ) - } - } +package com.approagency.drug.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import com.approagency.drug.presentation.screens.HomeScreen +import androidx.navigation.compose.composable +import com.approagency.drug.presentation.screens.RootScreen + +@Composable +fun AppNavGraph(navController: NavHostController, modifier: Modifier = Modifier) { + NavHost( + navController = navController , + startDestination = NavRoutes.Home.route + ){ + composable(NavRoutes.Home.route) { + RootScreen( + navHostController = navController , modifier = Modifier + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/navigation/NavRoutes.kt b/app/src/main/java/com/approagency/drug/navigation/NavRoutes.kt index 45712d4..a866313 100644 --- a/app/src/main/java/com/approagency/drug/navigation/NavRoutes.kt +++ b/app/src/main/java/com/approagency/drug/navigation/NavRoutes.kt @@ -1,9 +1,9 @@ -package com.approagency.drug.navigation - -abstract class NavRoutes (val route:String) { - object Home: NavRoutes("home") - object Detail: NavRoutes("detail/{cod}"){ - fun createRoute(cod: Int) = "detail/$cod" - } - object Lab: NavRoutes("lab") +package com.approagency.drug.navigation + +abstract class NavRoutes (val route:String) { + object Home: NavRoutes("home") + object Detail: NavRoutes("detail/{cod}"){ + fun createRoute(cod: Int) = "detail/$cod" + } + object Lab: NavRoutes("lab") } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/common/CustomBottonSheet.kt b/app/src/main/java/com/approagency/drug/presentation/common/CustomBottonSheet.kt index 03765a3..dda937a 100644 --- a/app/src/main/java/com/approagency/drug/presentation/common/CustomBottonSheet.kt +++ b/app/src/main/java/com/approagency/drug/presentation/common/CustomBottonSheet.kt @@ -1,39 +1,39 @@ -package com.approagency.drug.presentation.common - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.vada.caller.ui.theme.dime -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun CustomModalBottomSheet( - modifier: Modifier = Modifier, - sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), - scope: CoroutineScope = rememberCoroutineScope(), - onDismiss: (() -> Unit)? = null, - content: @Composable () -> Unit -) { - ModalBottomSheet( - onDismissRequest = { - onDismiss?.invoke() - }, - sheetState = sheetState, - modifier = modifier, - shape = RoundedCornerShape(topStart = MaterialTheme.dime.lg, topEnd = MaterialTheme.dime.lg) - ) { - Box( - modifier = Modifier - .fillMaxWidth() - ) { - content() - } - } -} +package com.approagency.drug.presentation.common + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.vada.caller.ui.theme.dime +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CustomModalBottomSheet( + modifier: Modifier = Modifier, + sheetState: SheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), + scope: CoroutineScope = rememberCoroutineScope(), + onDismiss: (() -> Unit)? = null, + content: @Composable () -> Unit +) { + ModalBottomSheet( + onDismissRequest = { + onDismiss?.invoke() + }, + sheetState = sheetState, + modifier = modifier, + shape = RoundedCornerShape(topStart = MaterialTheme.dime.lg, topEnd = MaterialTheme.dime.lg) + ) { + Box( + modifier = Modifier + .fillMaxWidth() + ) { + content() + } + } +} diff --git a/app/src/main/java/com/approagency/drug/presentation/common/CustomBox.kt b/app/src/main/java/com/approagency/drug/presentation/common/CustomBox.kt index 131da52..30b2a46 100644 --- a/app/src/main/java/com/approagency/drug/presentation/common/CustomBox.kt +++ b/app/src/main/java/com/approagency/drug/presentation/common/CustomBox.kt @@ -1,26 +1,26 @@ -package com.approagency.drug.presentation.common -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ModalBottomSheet -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip - - -@Composable - -fun CustomBox( - modifier: Modifier? = Modifier, - child: @Composable ()-> Unit -) { - if (modifier != null) { - Box( - modifier = modifier.clip(shape = MaterialTheme.shapes.large).background( - color = MaterialTheme.colorScheme.surfaceContainer - ) - ) { - child() - } - } +package com.approagency.drug.presentation.common +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip + + +@Composable + +fun CustomBox( + modifier: Modifier? = Modifier, + child: @Composable ()-> Unit +) { + if (modifier != null) { + Box( + modifier = modifier.clip(shape = MaterialTheme.shapes.large).background( + color = MaterialTheme.colorScheme.surfaceContainer + ) + ) { + child() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/common/CustomModalDialog.kt b/app/src/main/java/com/approagency/drug/presentation/common/CustomModalDialog.kt index 8d20cce..4374c01 100644 --- a/app/src/main/java/com/approagency/drug/presentation/common/CustomModalDialog.kt +++ b/app/src/main/java/com/approagency/drug/presentation/common/CustomModalDialog.kt @@ -1,40 +1,40 @@ -package com.approagency.drug.presentation.common - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.window.Dialog -import com.vada.caller.ui.theme.dime - -@Composable -fun CustomModalDialog( - onDismissRequest: () -> Unit, - modifier: Modifier = Modifier, - content: @Composable ColumnScope.() -> Unit -) { - Dialog( - onDismissRequest = onDismissRequest - ){ - Surface( - modifier = modifier - .wrapContentHeight(), - shape = MaterialTheme.shapes.medium, - color = MaterialTheme.colorScheme.surface, - tonalElevation = MaterialTheme.dime.sm - ) { - Column( - modifier = Modifier - .padding(MaterialTheme.dime.lg), - verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.lg), - content = content - ) - } - } +package com.approagency.drug.presentation.common + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.window.Dialog +import com.vada.caller.ui.theme.dime + +@Composable +fun CustomModalDialog( + onDismissRequest: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit +) { + Dialog( + onDismissRequest = onDismissRequest + ){ + Surface( + modifier = modifier + .wrapContentHeight(), + shape = MaterialTheme.shapes.medium, + color = MaterialTheme.colorScheme.surface, + tonalElevation = MaterialTheme.dime.sm + ) { + Column( + modifier = Modifier + .padding(MaterialTheme.dime.lg), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.dime.lg), + content = content + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/common/CustomTextFilled.kt b/app/src/main/java/com/approagency/drug/presentation/common/CustomTextFilled.kt new file mode 100644 index 0000000..b30bbd8 --- /dev/null +++ b/app/src/main/java/com/approagency/drug/presentation/common/CustomTextFilled.kt @@ -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 + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/common/SnackbarManager.kt b/app/src/main/java/com/approagency/drug/presentation/common/SnackbarManager.kt index 6aa3e3a..cafb1f4 100644 --- a/app/src/main/java/com/approagency/drug/presentation/common/SnackbarManager.kt +++ b/app/src/main/java/com/approagency/drug/presentation/common/SnackbarManager.kt @@ -1,21 +1,21 @@ -package com.approagency.drug.presentation.common - -import androidx.compose.material3.SnackbarHostState -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -object SnackbarManager { - private var snackbarHostState: SnackbarHostState? = null - private var coroutineScope: CoroutineScope? = null - - fun init(scope: CoroutineScope, hostState: SnackbarHostState) { - coroutineScope = scope - snackbarHostState = hostState - } - - fun showMessage(message: String) { - coroutineScope?.launch { - snackbarHostState?.showSnackbar(message) - } - } -} +package com.approagency.drug.presentation.common + +import androidx.compose.material3.SnackbarHostState +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +object SnackbarManager { + private var snackbarHostState: SnackbarHostState? = null + private var coroutineScope: CoroutineScope? = null + + fun init(scope: CoroutineScope, hostState: SnackbarHostState) { + coroutineScope = scope + snackbarHostState = hostState + } + + fun showMessage(message: String) { + coroutineScope?.launch { + snackbarHostState?.showSnackbar(message) + } + } +} diff --git a/app/src/main/java/com/approagency/drug/presentation/common/UiState.kt b/app/src/main/java/com/approagency/drug/presentation/common/UiState.kt index 84bbfdf..ed53f23 100644 --- a/app/src/main/java/com/approagency/drug/presentation/common/UiState.kt +++ b/app/src/main/java/com/approagency/drug/presentation/common/UiState.kt @@ -1,7 +1,7 @@ -package com.vada.caller.presentation.common - -sealed interface UiState { - object Loading : UiState - data class Success(val data: T) : UiState - data class Error(val message: String) : UiState +package com.vada.caller.presentation.common + +sealed interface UiState { + object Loading : UiState + data class Success(val data: T) : UiState + data class Error(val message: String) : UiState } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/common/loading.kt b/app/src/main/java/com/approagency/drug/presentation/common/loading.kt index f66a966..43debb5 100644 --- a/app/src/main/java/com/approagency/drug/presentation/common/loading.kt +++ b/app/src/main/java/com/approagency/drug/presentation/common/loading.kt @@ -1,25 +1,25 @@ -package com.approagency.drug.presentation.common - -import android.content.res.Resources.Theme -import androidx.compose.ui.graphics.Color -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.vada.caller.ui.theme.dime - -@Composable -fun Loading( -color: Color = MaterialTheme.colorScheme.onPrimary, -modifier: Modifier = Modifier -) { - CircularProgressIndicator( - color = color, - modifier = modifier, - strokeWidth = 2.dp - ) +package com.approagency.drug.presentation.common + +import android.content.res.Resources.Theme +import androidx.compose.ui.graphics.Color +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.vada.caller.ui.theme.dime + +@Composable +fun Loading( +color: Color = MaterialTheme.colorScheme.onPrimary, +modifier: Modifier = Modifier +) { + CircularProgressIndicator( + color = color, + modifier = modifier, + strokeWidth = 2.dp + ) } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/common/primaryButton.kt b/app/src/main/java/com/approagency/drug/presentation/common/primaryButton.kt index bb1ee46..6c1adae 100644 --- a/app/src/main/java/com/approagency/drug/presentation/common/primaryButton.kt +++ b/app/src/main/java/com/approagency/drug/presentation/common/primaryButton.kt @@ -1,39 +1,39 @@ -package com.approagency.drug.presentation.common - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp - -@Composable -fun PrimaryButton( - text: String, - isLoading: Boolean?, - onClick: () -> Unit, - height: Int? = null -) { - Button( - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.primary, - contentColor = MaterialTheme.colorScheme.onPrimary - ), - shape = MaterialTheme.shapes.medium, - modifier = Modifier.fillMaxWidth().height(height?.dp ?: 50.dp), - onClick = { - onClick() - }, - ) { - if (isLoading == true) { - Text("... در حال جستجو" , textAlign = TextAlign.Right) - } else { - Text(text) - } - } +package com.approagency.drug.presentation.common + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp + +@Composable +fun PrimaryButton( + text: String, + isLoading: Boolean?, + onClick: () -> Unit, + height: Int? = null +) { + Button( + colors = ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primary, + contentColor = MaterialTheme.colorScheme.onPrimary + ), + shape = MaterialTheme.shapes.medium, + modifier = Modifier.fillMaxWidth().height(height?.dp ?: 50.dp), + onClick = { + onClick() + }, + ) { + if (isLoading == true) { + Text("... در حال جستجو" , textAlign = TextAlign.Right) + } else { + Text(text) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/components/DarmanContent.kt b/app/src/main/java/com/approagency/drug/presentation/components/DarmanContent.kt index 24893c7..1cf685d 100644 --- a/app/src/main/java/com/approagency/drug/presentation/components/DarmanContent.kt +++ b/app/src/main/java/com/approagency/drug/presentation/components/DarmanContent.kt @@ -1,156 +1,131 @@ -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.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft -import androidx.compose.material.icons.filled.Search -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.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp -import com.approagency.drug.data.dto.DarmanList -import com.approagency.drug.data.dto.DarmanModel -import com.approagency.drug.data.dto.DrugDetail -import com.approagency.drug.data.dto.DrugListResponse -import com.approagency.drug.presentation.common.CustomBox -import com.approagency.drug.presentation.common.PrimaryButton -import com.vada.caller.ui.theme.dime -import kotlin.collections.emptyList - -@Composable -fun DarmanContent( - darmanData: Result?, - onDarmanClick: (Int) -> Unit, - onRetryClick:()-> Unit, -){ - val drugs = darmanData?.getOrNull()?.data ?: emptyList() - if (drugs.isEmpty()){ - CustomBox( - child = { - Column { - Row( - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxWidth() - .padding(all = MaterialTheme.dime.sm), - ) { - Text( - text = "مشکل در دریافت اطلاعات", - fontWeight = FontWeight.W500 - ) - Spacer(modifier = Modifier.width(MaterialTheme.dime.xs)) - Icon( - imageVector = Icons.Default.Search, - contentDescription = "Search" - ) - } - PrimaryButton( - text = "تلاش دوباره", - height = 45, - isLoading = false, - onClick = { - onRetryClick() - } - ) - } - } - ) - }else { - LazyColumn( - - ){ - items(drugs.count() , itemContent ={ - i -> DarmanItem( - darman = darmanData?.getOrNull()!!.data[i] , - onClick = { onDarmanClick(drugs[i].cod!!) } - ) - } ) - } - - } - - -} - -@Composable -fun DarmanItem( - darman: DarmanList, // Adjust this based on your actual data class - onClick: (Int?) -> Unit -) { - CustomBox( - modifier = Modifier - .padding(bottom = MaterialTheme.dime.sm) - .clickable( - indication = null, - interactionSource = remember { MutableInteractionSource() } - ) { - onClick(darman.cod) - }, - child = { - 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(darman.nam_fa.toString() , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right ) - Text(darman.nam_en.toString() , style = MaterialTheme.typography.bodySmall) - } - Row ( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center - ) { - TextButton( - onClick = { - onClick(darman.cod) - } - ) { - 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 -// } +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.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft +import androidx.compose.material.icons.filled.Search +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.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.approagency.drug.data.dto.DarmanList +import com.approagency.drug.data.dto.DarmanModel +import com.approagency.drug.data.dto.DrugDetail +import com.approagency.drug.data.dto.DrugListResponse +import com.approagency.drug.presentation.common.CustomBox +import com.approagency.drug.presentation.common.PrimaryButton +import com.vada.caller.ui.theme.dime +import kotlin.collections.emptyList + +@Composable +fun DarmanContent( + darmanData: Result?, + onDarmanClick: (Int) -> Unit, + onRetryClick:()-> Unit, +){ + val drugs = darmanData?.getOrNull()?.data ?: emptyList() + if (drugs.isEmpty()){ + RetryContent( + modifier = Modifier, + error = "مشکلی از سمت سرور پیش آمده", + onClick = { + onRetryClick() + } + ) + } else { + LazyColumn { + items(drugs.count() , itemContent ={ + i -> DarmanItem( + darman = darmanData?.getOrNull()!!.data[i] , + onClick = { onDarmanClick(drugs[i].cod!!) } + ) + } ) + } + + } + + +} + +@Composable +fun DarmanItem( + darman: DarmanList, // Adjust this based on your actual data class + onClick: (Int?) -> Unit +) { + CustomBox( + modifier = Modifier + .padding(bottom = MaterialTheme.dime.sm) + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { + onClick(darman.cod) + }, + child = { + 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(darman.nam_fa.toString() , style = MaterialTheme.typography.labelLarge , textAlign = TextAlign.Right ) + Text(darman.nam_en.toString() , style = MaterialTheme.typography.bodySmall) + } + Row ( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + TextButton( + onClick = { + onClick(darman.cod) + } + ) { + 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 +// } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/components/DrugListContent.kt b/app/src/main/java/com/approagency/drug/presentation/components/DrugListContent.kt index b392b62..5fe83e2 100644 --- a/app/src/main/java/com/approagency/drug/presentation/components/DrugListContent.kt +++ b/app/src/main/java/com/approagency/drug/presentation/components/DrugListContent.kt @@ -1,152 +1,152 @@ -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.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft -import androidx.compose.material.icons.filled.Search -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.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import com.approagency.drug.data.dto.DrugDetail -import com.approagency.drug.data.dto.DrugListResponse -import com.approagency.drug.presentation.common.CustomBox -import com.vada.caller.ui.theme.dime - -@Composable -fun DrugListContent( - drugsData: Result?, - onDrugClick: (Int) -> Unit -) { - val drugs = drugsData?.getOrNull()?.data ?: emptyList() - - if (drugs.isEmpty()) { - CustomBox( - child = { - Row( - horizontalArrangement = Arrangement.Center, - modifier = Modifier - .fillMaxWidth() - .padding(all = MaterialTheme.dime.sm), - ) { - Text( - text = "نتیجه‌ای یافت نشد", - fontWeight = FontWeight.W500 - ) - Spacer(modifier = Modifier.width(MaterialTheme.dime.xs)) - Icon( - imageVector = Icons.Default.Search, - contentDescription = "Search" - ) - } - } - ) - } else { - LazyColumn( - - ){ - items(drugs.count() , itemContent ={ - i -> DrugItem( - drug = drugs[i], - onClick = { onDrugClick(drugs[i].cod!!) } - ) - } ) - } - - } -} - - -@Composable -fun DrugItem( - drug: DrugDetail, // Adjust this based on your actual data class - onClick: (Int?) -> Unit -) { - CustomBox( - modifier = Modifier - .padding(bottom = MaterialTheme.dime.sm) - .fillMaxWidth() - .clickable( - indication = null, - interactionSource = remember { MutableInteractionSource() } - ) { - onClick(drug.cod) - }, - child = { - Row( - modifier = Modifier.padding(all = MaterialTheme.dime.lg), - ) { - Column ( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.Start - ) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text(drug.nam_en.toString() , style = MaterialTheme.typography.bodySmall) - Text(drug.nam_fa.toString() , style = MaterialTheme.typography.labelLarge,textAlign = TextAlign.Right) - } - if (drug.goroh_darmani != null ) - - - Row( - modifier = Modifier.fillMaxWidth() - , horizontalArrangement = Arrangement.End - ) {Text( - "گروه دارویی: ${drug.goroh_darmani.nam_fa.toString()}", - style = MaterialTheme.typography.labelMedium, - textAlign = TextAlign.Right - ) } - - - - Row ( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center - ) { - TextButton( - onClick = { - onClick(drug.cod) - } - ) { - 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 -// } +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.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft +import androidx.compose.material.icons.filled.Search +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.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import com.approagency.drug.data.dto.DrugDetail +import com.approagency.drug.data.dto.DrugListResponse +import com.approagency.drug.presentation.common.CustomBox +import com.vada.caller.ui.theme.dime + +@Composable +fun DrugListContent( + drugsData: Result?, + onDrugClick: (Int) -> Unit +) { + val drugs = drugsData?.getOrNull()?.data ?: emptyList() + + if (drugs.isEmpty()) { + CustomBox( + child = { + Row( + horizontalArrangement = Arrangement.Center, + modifier = Modifier + .fillMaxWidth() + .padding(all = MaterialTheme.dime.sm), + ) { + Text( + text = "نتیجه‌ای یافت نشد", + fontWeight = FontWeight.W500 + ) + Spacer(modifier = Modifier.width(MaterialTheme.dime.xs)) + Icon( + imageVector = Icons.Default.Search, + contentDescription = "Search" + ) + } + } + ) + } else { + LazyColumn( + + ){ + items(drugs.count() , itemContent ={ + i -> DrugItem( + drug = drugs[i], + onClick = { onDrugClick(drugs[i].cod!!) } + ) + } ) + } + + } +} + + +@Composable +fun DrugItem( + drug: DrugDetail, // Adjust this based on your actual data class + onClick: (Int?) -> Unit +) { + CustomBox( + modifier = Modifier + .padding(bottom = MaterialTheme.dime.sm) + .fillMaxWidth() + .clickable( + indication = null, + interactionSource = remember { MutableInteractionSource() } + ) { + onClick(drug.cod) + }, + child = { + Row( + modifier = Modifier.padding(all = MaterialTheme.dime.lg), + ) { + Column ( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.Start + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text(drug.nam_en.toString() , style = MaterialTheme.typography.bodySmall) + Text(drug.nam_fa.toString() , style = MaterialTheme.typography.labelLarge,textAlign = TextAlign.Right) + } + if (drug.goroh_darmani != null ) + + + Row( + modifier = Modifier.fillMaxWidth() + , horizontalArrangement = Arrangement.End + ) {Text( + "گروه دارویی: ${drug.goroh_darmani.nam_fa.toString()}", + style = MaterialTheme.typography.labelMedium, + textAlign = TextAlign.Right + ) } + + + + Row ( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center + ) { + TextButton( + onClick = { + onClick(drug.cod) + } + ) { + 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 +// } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/components/EmptyStateContent.kt b/app/src/main/java/com/approagency/drug/presentation/components/EmptyStateContent.kt index 6889bd5..96e2fc3 100644 --- a/app/src/main/java/com/approagency/drug/presentation/components/EmptyStateContent.kt +++ b/app/src/main/java/com/approagency/drug/presentation/components/EmptyStateContent.kt @@ -1,40 +1,40 @@ -package com.approagency.drug.presentation.components - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.Icon -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 com.approagency.drug.presentation.common.CustomBox -import com.vada.caller.ui.theme.dime - -@Composable -fun EmptyStateContent() { - Column( - modifier = Modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally - , verticalArrangement = Arrangement.Center - ) { - Icon( - imageVector = Icons.Default.Search, - contentDescription = "Search", - modifier = Modifier.size(MaterialTheme.dime.iconLg) - ) - CustomBox( - child = { - Text( - text = "برای جستجو، نام دارو را وارد کنید", - modifier = Modifier.padding(MaterialTheme.dime.sm) - ) - } - ) - } +package com.approagency.drug.presentation.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.Icon +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 com.approagency.drug.presentation.common.CustomBox +import com.vada.caller.ui.theme.dime + +@Composable +fun EmptyStateContent() { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally + , verticalArrangement = Arrangement.Center + ) { + Icon( + imageVector = Icons.Default.Search, + contentDescription = "Search", + modifier = Modifier.size(MaterialTheme.dime.iconLg) + ) + CustomBox( + child = { + Text( + text = "برای جستجو، نام دارو را وارد کنید", + modifier = Modifier.padding(MaterialTheme.dime.sm) + ) + } + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/components/RetryContent.kt b/app/src/main/java/com/approagency/drug/presentation/components/RetryContent.kt new file mode 100644 index 0000000..04f189c --- /dev/null +++ b/app/src/main/java/com/approagency/drug/presentation/components/RetryContent.kt @@ -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() } + ) + } + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/components/SearchResultContent.kt b/app/src/main/java/com/approagency/drug/presentation/components/SearchResultContent.kt new file mode 100644 index 0000000..62ffaee --- /dev/null +++ b/app/src/main/java/com/approagency/drug/presentation/components/SearchResultContent.kt @@ -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) + ) + } + } + } +} + diff --git a/app/src/main/java/com/approagency/drug/presentation/components/ShowDrugDetail.kt b/app/src/main/java/com/approagency/drug/presentation/components/ShowDrugDetail.kt index bf4f9bb..7e527d8 100644 --- a/app/src/main/java/com/approagency/drug/presentation/components/ShowDrugDetail.kt +++ b/app/src/main/java/com/approagency/drug/presentation/components/ShowDrugDetail.kt @@ -1,225 +1,225 @@ -package com.approagency.drug.presentation.components - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -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.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.ui.Modifier -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.LayoutDirection -import com.approagency.drug.data.dto.DrugDetail // Import DrugDetail directly -import com.approagency.drug.data.dto.DrugModels -import com.approagency.drug.presentation.common.CustomModalBottomSheet -import com.approagency.drug.presentation.viewModel.DrugDetailState -import com.approagency.drug.presentation.viewModel.HomeViewModel -import com.vada.caller.ui.theme.dime // Assuming this is your custom spacing/dimension object - -@Composable -@OptIn(ExperimentalMaterial3Api::class) -fun DrugDetailSheet( - viewModel: HomeViewModel, - state: DrugDetailState // Assuming you pass the state containing the detail -) { - val detail: DrugDetail? = state.drugDetail?.getOrNull()?.data - - // Only show the sheet if detail is not null - if (detail != null) { - CustomModalBottomSheet( - onDismiss = { - viewModel.closeDetail() - }, - content = { - // Use a LazyColumn for scrollable content inside the sheet - - CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { - LazyColumn( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = MaterialTheme.dime.lg), - contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg) // Add padding to the bottom of the content for better scrolling - ) { - // --- 1. Main Title --- - item { // Use item {} for single composables within LazyColumn - Text( - text = detail.nam_fa ?: "جزئیات دارو", - style = MaterialTheme.typography.headlineMedium, - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(bottom = MaterialTheme.dime.md) - ) - Text( - text = detail.nam_en ?: "N/A (English Name)", - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(bottom = MaterialTheme.dime.lg) - ) - - // Divider for visual separation - HorizontalDivider(Modifier.padding(bottom = MaterialTheme.dime.md)) - } - - // --- 2. Key Groupings --- - // Goroh Darmani (Pharmacological Group) - detail.goroh_darmani?.let { group -> - item { - DetailRow( - title = "گروه درمانی", - value = group.nam_fa ?: "نامشخص" - ) - } - group.nam_en?.let { enName -> - item { - DetailRow( - title = "Group (EN)", - value = enName - ) - } - } - } - - // Goroh Darmanei (Therapeutic Group) - detail.goroh_daroei?.let { group -> - item { - DetailRow( - title = "گروه دارویی", - value = group.nam ?: "نامشخص" - ) - } - } - - // Goroh Farmakologic (Pharmacological Class) - detail.goroh_farmakologic_cod?.let { cod -> - // 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. - item { - DetailRow( - title = "کلاس فارماکولوژیک", - value = cod.toString() - ) - } - } - - item { // Add a divider as a separate item - HorizontalDivider(Modifier.padding(vertical = MaterialTheme.dime.md)) - } - - // --- 3. Usage & Mechanism --- - detail.mavaredmasraf?.let { usage -> - item { - DetailSection(title = "موارد مصرف", content = usage) - } - } - - detail.mekanismtasir?.let { mechanism -> - item { - DetailSection(title = "مکانیسم اثر", content = mechanism) - } - } - - // --- 4. Warnings & Storage --- - detail.hoshdar?.let { warning -> - item { - DetailSection( - title = "هشدارها", - content = warning, - isWarning = true - ) - } - } - - detail.sharayetnegahdari?.let { storage -> - item { - DetailSection( - title = "شرایط نگهداری", - content = storage - ) - } - } - } - } - } - ) - } -} - -// --- Helper Composables for Clean Code --- - -@Composable -fun DetailRow( - title: String, - value: String -) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = MaterialTheme.dime.sm), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Text( - text = title, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant, - textAlign = TextAlign.Right - ) - Text( - text = value, - style = MaterialTheme.typography.bodyLarge, - fontWeight = FontWeight.SemiBold, - textAlign = TextAlign.End, - // 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. - modifier = Modifier.weight(1f, fill = false) - ) - } -} - -@Composable -fun DetailSection( - title: String, - content: String, - isWarning: Boolean = false -) { - Column( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = MaterialTheme.dime.md) - ) { - Text( - text = title, - style = MaterialTheme.typography.titleMedium, - color = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary, - fontWeight = FontWeight.Bold, - modifier = Modifier.padding(bottom = MaterialTheme.dime.sm) - ) - // Use a text block that respects line breaks (usually done with \n) - Text( - text = content, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurface, - // Consider adding softWrap = true if content might overflow - // softWrap = true - ) - } - // Add divider after each section for better visual separation - HorizontalDivider(Modifier.padding(top = MaterialTheme.dime.sm)) -} - -// Make sure your DrugDetailState in ViewModel looks something like this: -// data class DrugDetailState( -// val isLoading: Boolean = false, -// val drugDetail: DrugModels? = null, // Changed to DrugModels? to match data access -// val isDetailVisible: Boolean = false, -// val error: String? = null -// ) +package com.approagency.drug.presentation.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +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.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.ui.Modifier +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.LayoutDirection +import com.approagency.drug.data.dto.DrugDetail // Import DrugDetail directly +import com.approagency.drug.data.dto.DrugModels +import com.approagency.drug.presentation.common.CustomModalBottomSheet +import com.approagency.drug.presentation.viewModel.DrugDetailState +import com.approagency.drug.presentation.viewModel.HomeViewModel +import com.vada.caller.ui.theme.dime // Assuming this is your custom spacing/dimension object + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +fun DrugDetailSheet( + viewModel: HomeViewModel, + state: DrugDetailState // Assuming you pass the state containing the detail +) { + val detail: DrugDetail? = state.drugDetail?.getOrNull()?.data + + // Only show the sheet if detail is not null + if (detail != null) { + CustomModalBottomSheet( + onDismiss = { + viewModel.closeDetail() + }, + content = { + // Use a LazyColumn for scrollable content inside the sheet + + CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = MaterialTheme.dime.lg), + contentPadding = PaddingValues(bottom = MaterialTheme.dime.lg) // Add padding to the bottom of the content for better scrolling + ) { + // --- 1. Main Title --- + item { // Use item {} for single composables within LazyColumn + Text( + text = detail.nam_fa ?: "جزئیات دارو", + style = MaterialTheme.typography.headlineMedium, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(bottom = MaterialTheme.dime.md) + ) + Text( + text = detail.nam_en ?: "N/A (English Name)", + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(bottom = MaterialTheme.dime.lg) + ) + + // Divider for visual separation + HorizontalDivider(Modifier.padding(bottom = MaterialTheme.dime.md)) + } + + // --- 2. Key Groupings --- + // Goroh Darmani (Pharmacological Group) + detail.goroh_darmani?.let { group -> + item { + DetailRow( + title = "گروه درمانی", + value = group.nam_fa ?: "نامشخص" + ) + } + group.nam_en?.let { enName -> + item { + DetailRow( + title = "Group (EN)", + value = enName + ) + } + } + } + + // Goroh Darmanei (Therapeutic Group) + detail.goroh_daroei?.let { group -> + item { + DetailRow( + title = "گروه دارویی", + value = group.nam ?: "نامشخص" + ) + } + } + + // Goroh Farmakologic (Pharmacological Class) + detail.goroh_farmakologic_cod?.let { cod -> + // 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. + item { + DetailRow( + title = "کلاس فارماکولوژیک", + value = cod.toString() + ) + } + } + + item { // Add a divider as a separate item + HorizontalDivider(Modifier.padding(vertical = MaterialTheme.dime.md)) + } + + // --- 3. Usage & Mechanism --- + detail.mavaredmasraf?.let { usage -> + item { + DetailSection(title = "موارد مصرف", content = usage) + } + } + + detail.mekanismtasir?.let { mechanism -> + item { + DetailSection(title = "مکانیسم اثر", content = mechanism) + } + } + + // --- 4. Warnings & Storage --- + detail.hoshdar?.let { warning -> + item { + DetailSection( + title = "هشدارها", + content = warning, + isWarning = true + ) + } + } + + detail.sharayetnegahdari?.let { storage -> + item { + DetailSection( + title = "شرایط نگهداری", + content = storage + ) + } + } + } + } + } + ) + } +} + +// --- Helper Composables for Clean Code --- + +@Composable +fun DetailRow( + title: String, + value: String +) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = MaterialTheme.dime.sm), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = title, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant, + textAlign = TextAlign.Right + ) + Text( + text = value, + style = MaterialTheme.typography.bodyLarge, + fontWeight = FontWeight.SemiBold, + textAlign = TextAlign.End, + // 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. + modifier = Modifier.weight(1f, fill = false) + ) + } +} + +@Composable +fun DetailSection( + title: String, + content: String, + isWarning: Boolean = false +) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = MaterialTheme.dime.md) + ) { + Text( + text = title, + style = MaterialTheme.typography.titleMedium, + color = if (isWarning) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(bottom = MaterialTheme.dime.sm) + ) + // Use a text block that respects line breaks (usually done with \n) + Text( + text = content, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurface, + // Consider adding softWrap = true if content might overflow + // softWrap = true + ) + } + // Add divider after each section for better visual separation + HorizontalDivider(Modifier.padding(top = MaterialTheme.dime.sm)) +} + +// Make sure your DrugDetailState in ViewModel looks something like this: +// data class DrugDetailState( +// val isLoading: Boolean = false, +// val drugDetail: DrugModels? = null, // Changed to DrugModels? to match data access +// val isDetailVisible: Boolean = false, +// val error: String? = null +// ) diff --git a/app/src/main/java/com/approagency/drug/presentation/components/TestDetailSheet.kt b/app/src/main/java/com/approagency/drug/presentation/components/TestDetailSheet.kt new file mode 100644 index 0000000..aadf0cb --- /dev/null +++ b/app/src/main/java/com/approagency/drug/presentation/components/TestDetailSheet.kt @@ -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 + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/components/TestGroupItemContent.kt b/app/src/main/java/com/approagency/drug/presentation/components/TestGroupItemContent.kt new file mode 100644 index 0000000..2e96a6a --- /dev/null +++ b/app/src/main/java/com/approagency/drug/presentation/components/TestGroupItemContent.kt @@ -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("مشاهده جزییات") + } + } + } + } + + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/presentation/screens/HomeScreen.kt b/app/src/main/java/com/approagency/drug/presentation/screens/HomeScreen.kt index 3cdb403..465f900 100644 --- a/app/src/main/java/com/approagency/drug/presentation/screens/HomeScreen.kt +++ b/app/src/main/java/com/approagency/drug/presentation/screens/HomeScreen.kt @@ -1,292 +1,251 @@ -package com.approagency.drug.presentation.screens - -import android.R -import android.R.attr.fontWeight -import android.content.res.Resources -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.clickable -import androidx.compose.foundation.gestures.snapping.SnapPosition -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.foundation.shape.RoundedCornerShape -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.automirrored.twotone.KeyboardArrowLeft -import androidx.compose.material.icons.filled.Info -import androidx.compose.material.icons.filled.KeyboardArrowLeft -import androidx.compose.material.icons.filled.KeyboardArrowRight -import androidx.compose.material.icons.filled.Search -import androidx.compose.material.icons.twotone.Info -import androidx.compose.material.icons.twotone.KeyboardArrowLeft -import androidx.compose.material.icons.twotone.KeyboardArrowRight -import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TextField -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.platform.LocalSoftwareKeyboardController -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.TextUnit -import androidx.compose.ui.unit.dp -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavController -import com.approagency.drug.data.dto.DrugDetail -import com.approagency.drug.data.dto.DrugListResponse -import com.approagency.drug.domain.model.DrugSearchParams -import com.approagency.drug.presentation.common.CustomBox -import com.approagency.drug.presentation.common.CustomModalBottomSheet -import com.approagency.drug.presentation.common.CustomModalDialog -import com.approagency.drug.presentation.common.Loading -import com.approagency.drug.presentation.common.PrimaryButton -import com.approagency.drug.presentation.components.DarmanContent -import com.approagency.drug.presentation.components.DrugDetailSheet -import com.approagency.drug.presentation.components.DrugListContent -import com.approagency.drug.presentation.components.EmptyStateContent -import com.approagency.drug.presentation.viewModel.CombineState -import com.approagency.drug.presentation.viewModel.HomeEvent -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 org.koin.androidx.compose.koinViewModel - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun HomeScreen( - navController : NavController, - modifier: Modifier, - viewModel: HomeViewModel = koinViewModel() -) { - val state by viewModel.uiState.collectAsState() - val focusManager = LocalFocusManager.current - val keyboardController = LocalSoftwareKeyboardController.current - if (state.drugDetailState.isDetailVisible) { - DrugDetailSheet( - viewModel = viewModel, - state = state.drugDetailState - ) - } - HomeContent( - modifier = modifier, - state = state, - viewModelEvent = viewModel.event, - onSearch = { value -> - if (value is Int) { - viewModel.searchDrugs(DrugSearchParams(healGroup = value)) - } else if (value is String) { - viewModel.searchDrugs(DrugSearchParams(query = value)) - } - keyboardController?.hide() - focusManager.clearFocus()}, - onRetry = { - viewModel.getDarmani() - }, - onDrugClick = { drugId -> viewModel.drugDetail(drugId , allDrugs = state.drugSearchState.drugsData) } - ) -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun HomeContent( - state: CombineState, - viewModelEvent: SharedFlow, - onSearch: (Any) -> Unit, - onRetry: () -> Unit, - onDrugClick: (Int) -> Unit, - modifier: Modifier = Modifier -) { - var search by remember { mutableStateOf("") } - Column( - modifier = modifier - .fillMaxSize().padding(horizontal = MaterialTheme.dime.md) - .padding(top = MaterialTheme.dime.md), - horizontalAlignment = Alignment.Start, - ) { - Column ( - verticalArrangement = Arrangement.Center - ){ - CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl) { - Box( - modifier = Modifier - .fillMaxWidth() - .height(45.dp) // Your desired height - .background( - color = MaterialTheme.colorScheme.surfaceVariant, - shape =MaterialTheme.shapes.medium - ) - .border( - width = 1.dp, - color = MaterialTheme.colorScheme.primaryContainer, - shape =MaterialTheme.shapes.medium - ) - .padding(horizontal = MaterialTheme.dime.md, vertical = MaterialTheme.dime.sm) - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxSize() - ) { - BasicTextField( - value = search, - onValueChange = { search = it }, - modifier = Modifier.weight(1f), - singleLine = true, - textStyle = MaterialTheme.typography.bodyLarge, - decorationBox = { innerTextField -> - Box { - if (search.isEmpty()) { - Text( - "جستجوی دارو", - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - innerTextField() - } - } - ) - IconButton( - onClick = { - if (search.isNotBlank()) { - onSearch(search) - } - }, - modifier = Modifier.size(32.dp) - ) { - Icon( - imageVector = Icons.Default.Search, - contentDescription = "Search", - modifier = Modifier.size(18.dp) - ) - } - } - } - } - Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) - PrimaryButton( - text = "جستجو", - height = 40, - isLoading = state.drugSearchState.isLoading, - onClick = { - if (search.isNotBlank()) { - onSearch(search) - } - } - ) - Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) - Box(modifier = Modifier.fillMaxSize()) { - - // Always draw the data if available - if (state.drugSearchState.drugsData != null && !state.showDarmanList) { - Column { - Row( - horizontalArrangement = Arrangement.End, - modifier = Modifier.fillMaxWidth() - ) { - TextButton( - onClick = { - onRetry() - } - ) { - Text("مشاهده تمامی گروه های دارویی") - Icon( - imageVector = Icons.Default.KeyboardArrowRight, - contentDescription = "back" - ) - Spacer(modifier = Modifier.width(MaterialTheme.dime.sm)) - } - } - DrugListContent( - drugsData = state.drugSearchState.drugsData, - onDrugClick = onDrugClick - ) - } - } - else if (state.darmanState.getDarmani != null ){ - 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)) - ) - } - } - - - } - } -} - - - - - +package com.approagency.drug.presentation.screens + +import android.R +import android.R.attr.fontWeight +import android.content.res.Resources +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.snapping.SnapPosition +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.shape.RoundedCornerShape +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.automirrored.twotone.KeyboardArrowLeft +import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.KeyboardArrowLeft +import androidx.compose.material.icons.filled.KeyboardArrowRight +import androidx.compose.material.icons.filled.Search +import androidx.compose.material.icons.twotone.Info +import androidx.compose.material.icons.twotone.KeyboardArrowLeft +import androidx.compose.material.icons.twotone.KeyboardArrowRight +import androidx.compose.material3.Button +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavController +import com.approagency.drug.data.dto.DrugDetail +import com.approagency.drug.data.dto.DrugListResponse +import com.approagency.drug.domain.model.DrugSearchParams +import com.approagency.drug.presentation.common.CustomBox +import com.approagency.drug.presentation.common.CustomModalBottomSheet +import com.approagency.drug.presentation.common.CustomModalDialog +import com.approagency.drug.presentation.common.CustomTextFilled +import com.approagency.drug.presentation.common.Loading +import com.approagency.drug.presentation.common.PrimaryButton +import com.approagency.drug.presentation.components.DarmanContent +import com.approagency.drug.presentation.components.DrugDetailSheet +import com.approagency.drug.presentation.components.DrugListContent +import com.approagency.drug.presentation.components.EmptyStateContent +import com.approagency.drug.presentation.viewModel.CombineState +import com.approagency.drug.presentation.viewModel.HomeEvent +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 org.koin.androidx.compose.koinViewModel + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeScreen( + navController : NavController, + modifier: Modifier, + viewModel: HomeViewModel = koinViewModel() +) { + val state by viewModel.uiState.collectAsState() + val focusManager = LocalFocusManager.current + val keyboardController = LocalSoftwareKeyboardController.current + if (state.drugDetailState.isDetailVisible) { + DrugDetailSheet( + viewModel = viewModel, + state = state.drugDetailState + ) + } + HomeContent( + modifier = modifier, + state = state, + viewModelEvent = viewModel.event, + onSearch = { value -> + if (value is Int) { + viewModel.searchDrugs(DrugSearchParams(healGroup = value)) + } else if (value is String) { + viewModel.searchDrugs(DrugSearchParams(query = value)) + } + keyboardController?.hide() + focusManager.clearFocus()}, + onRetry = { + viewModel.getDarmani() + }, + onDrugClick = { drugId -> viewModel.drugDetail(drugId , allDrugs = state.drugSearchState.drugsData) } + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HomeContent( + state: CombineState, + viewModelEvent: SharedFlow, + onSearch: (Any) -> Unit, + onRetry: () -> Unit, + onDrugClick: (Int) -> Unit, + modifier: Modifier = Modifier +) { + var searchText by remember { mutableStateOf("") } + Column( + modifier = modifier + .fillMaxSize().padding(horizontal = MaterialTheme.dime.md) + .padding(top = MaterialTheme.dime.md), + horizontalAlignment = Alignment.Start, + ) { + Column ( + verticalArrangement = Arrangement.Center + ){ + CustomTextFilled( + value = searchText, + onValueChange = { searchText = it }, + onSearch = { query -> + if (query.isNotBlank()) { + onSearch(query) + } + }, + placeholder = "جستجوی دارو", + showClearButton = true, + showSearchButton = true, + autoSearch = false, // Set to true if you want search while typing + height = 45 + ) + Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) + PrimaryButton( + text = "جستجو", + height = 40, + isLoading = state.drugSearchState.isLoading, + onClick = { + if (searchText.isNotBlank()) { + onSearch(searchText) + } + } + ) + Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) + Box(modifier = Modifier.fillMaxSize()) { + + // Always draw the data if available + if (state.drugSearchState.drugsData != null && !state.showDarmanList) { + Column { + Row( + horizontalArrangement = Arrangement.End, + modifier = Modifier.fillMaxWidth() + ) { + TextButton( + onClick = { + onRetry() + } + ) { + Text("مشاهده تمامی گروه های دارویی") + Icon( + imageVector = Icons.Default.KeyboardArrowRight, + contentDescription = "back" + ) + Spacer(modifier = Modifier.width(MaterialTheme.dime.sm)) + } + } + DrugListContent( + drugsData = state.drugSearchState.drugsData, + onDrugClick = onDrugClick + ) + } + } + else if (state.darmanState.getDarmani != null ){ + 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)) + ) + } + } + + + } + } +} + + + + + diff --git a/app/src/main/java/com/approagency/drug/presentation/screens/LabScreen.kt b/app/src/main/java/com/approagency/drug/presentation/screens/LabScreen.kt index 9db7956..99c297e 100644 --- a/app/src/main/java/com/approagency/drug/presentation/screens/LabScreen.kt +++ b/app/src/main/java/com/approagency/drug/presentation/screens/LabScreen.kt @@ -1,185 +1,207 @@ -package com.approagency.drug.presentation.screens - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.text.style.TextDirection -import androidx.compose.ui.unit.dp -import com.approagency.drug.domain.model.TestGroup -import com.approagency.drug.presentation.common.CustomBox -import com.approagency.drug.presentation.common.CustomModalDialog -import com.approagency.drug.presentation.common.Loading -import com.approagency.drug.presentation.common.PrimaryButton -import com.approagency.drug.presentation.viewModel.LabViewModel -import com.vada.caller.ui.theme.dime -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.count -import org.koin.androidx.compose.koinViewModel - -@Composable -fun LabScreen( - modifier: Modifier = Modifier, - viewModel: LabViewModel = koinViewModel() -) { - val testGroupsState by viewModel.testGroups.collectAsState() - val isLoading = testGroupsState.isLoading - val error = testGroupsState.error - val testGroups = testGroupsState.testGroup - - Column(modifier = modifier.fillMaxSize()) { - if (isLoading) { - CustomModalDialog( - onDismissRequest = { /* maybe disable dismiss while loading */ }, - content = { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Loading(color = MaterialTheme.colorScheme.primary) - Spacer(modifier = Modifier.height(MaterialTheme.dime.sm)) - Text("در حال جستجو..." , textAlign = TextAlign.Right) - } - }, -// modifier = Modifier -// .matchParentSize() -// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f)) - ) - } - else if(error != null){ - CustomBox( - child = { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(MaterialTheme.dime.md) - ) { - Text( - text = "خطا در دریافت اطلاعات", - style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.error - ) - Text( - text = error, - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(vertical = 8.dp) - ) - PrimaryButton( - text = "تلاش مجدد", - height = 40, - isLoading = false, - onClick = { viewModel.loadTestGroups() } - ) - } - } - ) - } - else if (testGroups != null){ - TestGroupsList( - testGroupsFlow = testGroups, - viewModel = viewModel - ) - } else { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Text("هیچ داده‌ای موجود نیست") - } - } - - } -} - -@Composable -fun TestGroupsList( - testGroupsFlow: Flow>, - viewModel: LabViewModel -) { - val testGroups by testGroupsFlow.collectAsState(initial = emptyList()) - - if (testGroups.isEmpty()) { - CustomModalDialog( - onDismissRequest = { /* maybe disable dismiss while loading */ }, - content = { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Loading(color = MaterialTheme.colorScheme.primary) - Spacer(modifier = Modifier.height(MaterialTheme.dime.sm)) - Text("در حال جستجو..." , textAlign = TextAlign.Right) - } - }, -// modifier = Modifier -// .matchParentSize() -// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f)) - ) - } else { - LazyColumn( - modifier = Modifier.fillMaxSize() - ) { - items(testGroups) { group -> - TestGroupItem( - group = group, - onClick = { - // Navigate to test items of this group -// viewModel.loadTestItemsByGroup(group.id) - } - ) - } - } - } -} - -@Composable -fun TestGroupItem( - group: TestGroup, - onClick: () -> Unit -) { - CustomBox ( - modifier = Modifier - .padding(bottom = MaterialTheme.dime.sm) - .clickable( - indication = null, - interactionSource = remember { MutableInteractionSource() } - ) { - onClick() - }, - ) { - Column ( - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.Start - ) { - Text( - text = group.fname ?: group.ename ?: "بدون نام", - style = MaterialTheme.typography.titleMedium.copy( - textAlign = TextAlign.Right, - textDirection = TextDirection.Rtl - ), - modifier = Modifier.padding(vertical = MaterialTheme.dime.xs) - ) - group.detail?.let { - Text( - text = it, - style = MaterialTheme.typography.bodySmall.copy( - textAlign = TextAlign.Right , - textDirection = TextDirection.Rtl - ), - color = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.padding(horizontal = MaterialTheme.dime.lg) - ) - } - } - } -} \ No newline at end of file +package com.approagency.drug.presentation.screens + +import android.R.attr.onClick +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.twotone.KeyboardArrowLeft +import androidx.compose.material3.CircularProgressIndicator +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.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.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 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.CustomModalDialog +import com.approagency.drug.presentation.common.CustomTextFilled +import com.approagency.drug.presentation.common.Loading +import com.approagency.drug.presentation.common.PrimaryButton +import com.approagency.drug.presentation.components.RetryContent +import com.approagency.drug.presentation.components.SearchResultsContent +import com.approagency.drug.presentation.components.TestDetailSheet +import com.approagency.drug.presentation.components.TestGroupItemContent +import com.approagency.drug.presentation.viewModel.LabViewModel +import com.vada.caller.ui.theme.dime +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.count +import org.koin.androidx.compose.koinViewModel + +@Composable +fun LabScreen( + modifier: Modifier = Modifier, + viewModel: LabViewModel = koinViewModel() +) { + val testGroupsState by viewModel.testGroups.collectAsState() + val testItemsState by viewModel.testItems.collectAsState() + val searchResultsState by viewModel.searchResults.collectAsState() + + val isLoading = testGroupsState.isLoading || testItemsState.isLoading || searchResultsState.isLoading + val error = testGroupsState.error ?: searchResultsState.error + val testGroups = testGroupsState.testGroup + + var selectedGroup by remember { mutableStateOf(null) } + var selectedItem by remember { mutableStateOf(null) } + var showSheet by remember { mutableStateOf(false) } + var searchText by remember { mutableStateOf("") } + Column(modifier = modifier.fillMaxSize().padding(horizontal = MaterialTheme.dime.md) + .padding(top = MaterialTheme.dime.md),) { + CustomTextFilled( + value = searchText, + onValueChange = { + searchText = it + viewModel.updateSearchQuery(it) + }, + onSearch = { query -> + if (query.isNotBlank()) { + viewModel.updateSearchQuery(query) + } + }, + placeholder = "جستجو در گروه‌ها و آیتم‌های آزمایشگاهی", + showClearButton = true, + showSearchButton = true, + autoSearch = true, + height = 45 + ) + Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) + PrimaryButton( + text = "جستجو", + height = 40, + isLoading =false, + onClick = {} + ) + Spacer(modifier = Modifier.height(MaterialTheme.dime.xs)) + if (searchResultsState.query.isNotBlank()) { + // Search Results Section + SearchResultsContent( + searchResult = searchResultsState.searchResults, + isLoading = searchResultsState.isLoading, + error = searchResultsState.error, + onGroupClick = { group -> + selectedGroup = group + viewModel.getItemByGroupId(group.id) + showSheet = true + }, + onItemClick = { item -> + selectedItem = item + // You can show item detail sheet here + // For now, we'll show the parent group + viewModel.getItemByGroupId(item.groupId) + showSheet = true + }, + onClearSearch = { + searchText = "" + viewModel.clearSearch() + viewModel.loadTestGroups() + } + ) + } else if(error != null){ + RetryContent(modifier = modifier , error = error , onClick = { + viewModel.loadTestGroups() + }) + } + else if (testGroups != null){ + TestGroupsList( + testGroupsFlow = testGroups, + viewModel = viewModel, + onGroupClick = { group -> + selectedGroup = group + viewModel.getItemByGroupId(group.id) + showSheet = true + } + ) + } + else { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text("هیچ داده‌ای موجود نیست") + } + } + + } + if (isLoading) { + CustomModalDialog( + onDismissRequest = { /* maybe disable dismiss while loading */ }, + content = { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Loading(color = MaterialTheme.colorScheme.primary) + Spacer(modifier = Modifier.height(MaterialTheme.dime.sm)) + Text("در حال جستجو..." , textAlign = TextAlign.Right) + } + }, +// modifier = Modifier +// .matchParentSize() +// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f)) + ) + } + if (showSheet && selectedGroup != null) { + TestDetailSheet( + state = testItemsState, + testGroup = selectedGroup!!, + onDismiss = { showSheet = false } + ) + } +} + +@Composable +fun TestGroupsList( + testGroupsFlow: Flow>, + viewModel: LabViewModel, + onGroupClick: (TestGroup) -> Unit +) { + val testGroups by testGroupsFlow.collectAsState(initial = emptyList()) + + if (testGroups.isEmpty()) { + CustomModalDialog( + onDismissRequest = { /* maybe disable dismiss while loading */ }, + content = { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + Loading(color = MaterialTheme.colorScheme.primary) + Spacer(modifier = Modifier.height(MaterialTheme.dime.sm)) + Text("در حال جستجو..." , textAlign = TextAlign.Right) + } + }, +// modifier = Modifier +// .matchParentSize() +// .background(MaterialTheme.colorScheme.background.copy(alpha = 0.5f)) + ) + } else { + LazyColumn( + modifier = Modifier.fillMaxSize() + .fillMaxSize().padding( vertical =MaterialTheme.dime.md ) + ) { + items(testGroups) { group -> + TestGroupItemContent( + group = group, + onClick = { onGroupClick(group) } + ) + } + } + } +} diff --git a/app/src/main/java/com/approagency/drug/presentation/screens/RootScreen.kt b/app/src/main/java/com/approagency/drug/presentation/screens/RootScreen.kt index 992c7de..d39df5d 100644 --- a/app/src/main/java/com/approagency/drug/presentation/screens/RootScreen.kt +++ b/app/src/main/java/com/approagency/drug/presentation/screens/RootScreen.kt @@ -1,128 +1,126 @@ -package com.approagency.drug.presentation.screens - -import android.annotation.SuppressLint -import android.app.Activity -import android.graphics.drawable.Icon -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.WindowInsetsSides -import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.navigationBars -import androidx.compose.foundation.layout.only -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawingPadding -import androidx.compose.foundation.layout.size -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Build -import androidx.compose.material.icons.filled.Search -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarDefaults -import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.NavigationBarItemDefaults -import androidx.compose.material3.Scaffold -import androidx.compose.material3.ScaffoldDefaults -import androidx.compose.material3.SegmentedButtonDefaults.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults -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.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import androidx.navigation.NavHostController -@SuppressLint("ConfigurationScreenWidthHeight") -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun RootScreen(navHostController: NavHostController, modifier: Modifier){ - val pages = listOf( - "دارو" to Icons.Default.Search, - "آزمایش" to Icons.Default.Build - ) - var seletedTab by remember { mutableStateOf(0) } - val context = LocalContext.current - val activity = context as Activity - - Scaffold ( - contentWindowInsets = ScaffoldDefaults.contentWindowInsets, - topBar = { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl){ - TopAppBar( - colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - titleContentColor = MaterialTheme.colorScheme.primary, - ), - title = { - Text("جستجوی دارویی" , textAlign = TextAlign.Right , style = MaterialTheme.typography.titleLarge , fontWeight = FontWeight.W700 ) - } - ) - }}, -// modifier = modifier.fillMaxSize(), - bottomBar = { - NavigationBar( - - containerColor = MaterialTheme.colorScheme.surface, - modifier = Modifier - .fillMaxWidth() - .height(110.dp).padding(bottom = 2.dp).clip(MaterialTheme.shapes.extraLarge), - tonalElevation = 8.dp, // subtle shadow - windowInsets = WindowInsets.navigationBars - .only(WindowInsetsSides.Bottom) - ) { - pages.forEachIndexed { index, item -> - val selected = seletedTab == index - - NavigationBarItem( - colors = NavigationBarItemDefaults.colors( - selectedIconColor = MaterialTheme.colorScheme.onPrimary, - selectedTextColor = MaterialTheme.colorScheme.primary, - indicatorColor = MaterialTheme.colorScheme.primary, - unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant, - unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant - ), - selected = selected, - onClick = { seletedTab = index }, - - icon = { - Icon( - imageVector = item.second, - contentDescription = item.first, - modifier = Modifier.size(16.dp) - ) - }, - - label = { - Text( - item.first, - style = MaterialTheme.typography.labelMedium - ) - }, - - alwaysShowLabel = true - ) - } - } - } - ) { - padding -> when (seletedTab) { - 0 -> HomeScreen( navController = navHostController,Modifier.padding(padding).consumeWindowInsets(padding)) - 1 -> LabScreen(Modifier.padding(padding)) - } - } -} +package com.approagency.drug.presentation.screens + +import android.annotation.SuppressLint +import android.app.Activity +import android.graphics.drawable.Icon +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Build +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.NavigationBar +import androidx.compose.material3.NavigationBarDefaults +import androidx.compose.material3.NavigationBarItem +import androidx.compose.material3.NavigationBarItemDefaults +import androidx.compose.material3.Scaffold +import androidx.compose.material3.ScaffoldDefaults +import androidx.compose.material3.SegmentedButtonDefaults.Icon +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +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.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.LayoutDirection +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +@SuppressLint("ConfigurationScreenWidthHeight") +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun RootScreen(navHostController: NavHostController, modifier: Modifier){ + val pages = listOf( + "دارو" to Icons.Default.Search, + "آزمایش" to Icons.Default.Build + ) + var seletedTab by remember { mutableStateOf(0) } + val context = LocalContext.current + val activity = context as Activity + + Scaffold ( + contentWindowInsets = ScaffoldDefaults.contentWindowInsets, + topBar = { CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl){ + TopAppBar( + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + titleContentColor = MaterialTheme.colorScheme.primary, + ), + title = { + Text("دستیار سلامت" , textAlign = TextAlign.Right , style = MaterialTheme.typography.titleLarge , fontWeight = FontWeight.W700 ) + } + ) + }}, + bottomBar = { + NavigationBar( + containerColor = MaterialTheme.colorScheme.surface, + modifier = Modifier + .fillMaxWidth() + .height(110.dp).padding(bottom = 2.dp).clip(MaterialTheme.shapes.extraLarge), + tonalElevation = 8.dp, // subtle shadow + windowInsets = WindowInsets.navigationBars + .only(WindowInsetsSides.Bottom) + ) { + pages.forEachIndexed { index, item -> + val selected = seletedTab == index + + NavigationBarItem( + colors = NavigationBarItemDefaults.colors( + selectedIconColor = MaterialTheme.colorScheme.onPrimary, + selectedTextColor = MaterialTheme.colorScheme.primary, + indicatorColor = MaterialTheme.colorScheme.primary, + unselectedIconColor = MaterialTheme.colorScheme.onSurfaceVariant, + unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant + ), + selected = selected, + onClick = { seletedTab = index }, + + icon = { + Icon( + imageVector = item.second, + contentDescription = item.first, + modifier = Modifier.size(16.dp) + ) + }, + + label = { + Text( + item.first, + style = MaterialTheme.typography.labelMedium + ) + }, + + alwaysShowLabel = true + ) + } + } + } + ) { + padding -> when (seletedTab) { + 0 -> HomeScreen( navController = navHostController,Modifier.padding(padding).consumeWindowInsets(padding)) + 1 -> LabScreen(Modifier.padding(padding)) + } + } +} diff --git a/app/src/main/java/com/approagency/drug/presentation/viewModel/HomeViewModel.kt b/app/src/main/java/com/approagency/drug/presentation/viewModel/HomeViewModel.kt index f873cfe..a62cc2d 100644 --- a/app/src/main/java/com/approagency/drug/presentation/viewModel/HomeViewModel.kt +++ b/app/src/main/java/com/approagency/drug/presentation/viewModel/HomeViewModel.kt @@ -1,165 +1,187 @@ -package com.approagency.drug.presentation.viewModel - -import android.net.http.HttpException -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.approagency.drug.data.dto.DarmanModel -import com.approagency.drug.data.dto.DrugDetail -import com.approagency.drug.data.dto.DrugListResponse -import com.approagency.drug.data.dto.DrugModels -import com.approagency.drug.domain.model.DrugSearchParams -import com.approagency.drug.domain.usecase.GetDarmanUseCase -import com.approagency.drug.domain.usecase.GetDrugDetailUseCase -import com.approagency.drug.domain.usecase.GetDrugSearchUseCase -import com.approagency.drug.presentation.screens.HomeScreen -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch - -class HomeViewModel ( - private val getDrugSearchUseCase: GetDrugSearchUseCase, - private val getDrugDetailUseCase: GetDrugDetailUseCase, - private val getDarmanUseCase: GetDarmanUseCase -): ViewModel() { - private val _uiState = MutableStateFlow(CombineState()) - val uiState : StateFlow = _uiState - - // Add SharedFlow for one-time events - private val _event = MutableSharedFlow() - val event = _event.asSharedFlow() - init { - getDarmani() - } - fun getDarmani(){ - _uiState.update { it.copy(darmanState = it.darmanState.copy( - isLoading = true - ) , showDarmanList = true ) } - viewModelScope.launch { - try { - val result = getDarmanUseCase.invoke() - _uiState.update { - it.copy( - darmanState = it.darmanState.copy( - isLoading = false, - getDarmani = result - ) - ) - } - } catch (e: HttpException){ - handleError(e.message) - } - } - } - - fun searchDrugs(drugSearchParams: DrugSearchParams) { - _uiState.update { it.copy(drugSearchState = it.drugSearchState.copy( - isLoading = true - ) , showDarmanList = false) } - viewModelScope.launch { - try { - val result = getDrugSearchUseCase.invoke(drugSearchParams) - _uiState.update { it.copy( - drugSearchState = it.drugSearchState.copy( - isLoading = false, - drugsData = result - ) - - ) } - } catch (e: HttpException) { - handleError(e.message) - }catch (e: Exception) { - handleError(e.localizedMessage ?: "Unknown error") - } - } - } - - fun drugDetail(cod: Int , allDrugs:Result?) { - _uiState.update { it.copy(drugDetailState = it.drugDetailState.copy( - isLoading = true - ) , showDarmanList = false) } - viewModelScope.launch { - try { - val result = getDrugDetailUseCase.invoke(cod) - _uiState.update { - it.copy( - drugDetailState = it.drugDetailState.copy( - isLoading = false, - drugDetail = result, - isDetailVisible = true - ), - drugSearchState = it.drugSearchState.copy( - isLoading = false, - drugsData = allDrugs - ) - ) - } - println(result) - } catch (e: HttpException) { - handleError(e.message) - }catch (e: Exception) { - handleError(e.localizedMessage ?: "Unknown error") - } - } - } - fun closeDetail() { - _uiState.update { - it.copy( - drugDetailState = it.drugDetailState.copy( - isDetailVisible = false, - drugDetail = null // optional, depends if you want to keep last detail cached - ) - ) - } - } - - private suspend fun handleError(message: String?) { - _uiState.update { it.copy(drugSearchState = it.drugSearchState.copy( - isLoading = false - ) , drugDetailState = it.drugDetailState.copy( - isLoading = false - )) } - _event.emit(HomeEvent.ShowError(message ?: "Error occurred")) - } - - // Clear error when consumed - fun clearError() { -// _uiState.update { it.copy(error = null) } - } -} - - -sealed class HomeEvent { - data class ShowError(val message: String) : HomeEvent() - data class ShowDetailDrug(val drugDetail:Result?): HomeEvent() -} - -data class CombineState( - val drugSearchState: DrugSearchState = DrugSearchState() , - val drugDetailState: DrugDetailState =DrugDetailState(), - val darmanState: GetDarmaniState = GetDarmaniState(), - val showDarmanList: Boolean = true - ) - -data class DrugSearchState( - val isLoading: Boolean = false, - val drugsData: Result? = null, - val drugDetail: Result? = null, - val error:String? = null -) - -data class DrugDetailState( - val isLoading: Boolean = false, - val drugDetail:Result? = null, - val isDetailVisible: Boolean = false, - val error:String? = null -) - -data class GetDarmaniState( - val isLoading: Boolean = false, - val getDarmani:Result? = null, - val error:String? = null -) - +package com.approagency.drug.presentation.viewModel + +import android.net.http.HttpException +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.approagency.drug.data.dto.DarmanModel +import com.approagency.drug.data.dto.DrugDetail +import com.approagency.drug.data.dto.DrugListResponse +import com.approagency.drug.data.dto.DrugModels +import com.approagency.drug.domain.model.DrugSearchParams +import com.approagency.drug.domain.usecase.GetDarmanUseCase +import com.approagency.drug.domain.usecase.GetDrugDetailUseCase +import com.approagency.drug.domain.usecase.GetDrugSearchUseCase +import com.approagency.drug.presentation.screens.HomeScreen +import com.approagency.drug.utils.retryWithBackoff +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class HomeViewModel ( + private val getDrugSearchUseCase: GetDrugSearchUseCase, + private val getDrugDetailUseCase: GetDrugDetailUseCase, + private val getDarmanUseCase: GetDarmanUseCase +): ViewModel() { + private val _uiState = MutableStateFlow(CombineState()) + val uiState : StateFlow = _uiState + + // Add SharedFlow for one-time events + private val _event = MutableSharedFlow() + val event = _event.asSharedFlow() + init { + getDarmani() + } + fun getDarmani() { + _uiState.update { + it.copy( + darmanState = it.darmanState.copy(isLoading = true), + showDarmanList = true + ) + } + viewModelScope.launch { + retryWithBackoff( + maxRetries = 3, + onRetry = { attempt, delay -> + // Optional: log retry attempt + println("Retrying getDarmani, attempt $attempt, delay $delay ms") + } + ) { + getDarmanUseCase.invoke() + }.fold( + onSuccess = { result -> + _uiState.update { + it.copy( + darmanState = it.darmanState.copy( + isLoading = false, + getDarmani = result + ) + ) + } + }, + onFailure = { error -> + handleError(error.message ?: "Unknown error") + _uiState.update { + it.copy( + darmanState = it.darmanState.copy( + isLoading = false, + getDarmani = Result.failure(error) + ) + ) + } + } + ) + } + } + + fun searchDrugs(drugSearchParams: DrugSearchParams) { + _uiState.update { it.copy(drugSearchState = it.drugSearchState.copy( + isLoading = true + ) , showDarmanList = false) } + viewModelScope.launch { + try { + val result = getDrugSearchUseCase.invoke(drugSearchParams) + _uiState.update { it.copy( + drugSearchState = it.drugSearchState.copy( + isLoading = false, + drugsData = result + ) + + ) } + } catch (e: HttpException) { + handleError(e.message) + }catch (e: Exception) { + handleError(e.localizedMessage ?: "Unknown error") + } + } + } + + fun drugDetail(cod: Int , allDrugs:Result?) { + _uiState.update { it.copy(drugDetailState = it.drugDetailState.copy( + isLoading = true + ) , showDarmanList = false) } + viewModelScope.launch { + try { + val result = getDrugDetailUseCase.invoke(cod) + _uiState.update { + it.copy( + drugDetailState = it.drugDetailState.copy( + isLoading = false, + drugDetail = result, + isDetailVisible = true + ), + drugSearchState = it.drugSearchState.copy( + isLoading = false, + drugsData = allDrugs + ) + ) + } + println(result) + } catch (e: HttpException) { + handleError(e.message) + }catch (e: Exception) { + handleError(e.localizedMessage ?: "Unknown error") + } + } + } + fun closeDetail() { + _uiState.update { + it.copy( + drugDetailState = it.drugDetailState.copy( + isDetailVisible = false, + drugDetail = null // optional, depends if you want to keep last detail cached + ) + ) + } + } + + private suspend fun handleError(message: String?) { + _uiState.update { it.copy(drugSearchState = it.drugSearchState.copy( + isLoading = false + ) , drugDetailState = it.drugDetailState.copy( + isLoading = false + )) } + _event.emit(HomeEvent.ShowError(message ?: "Error occurred")) + } + + // Clear error when consumed + fun clearError() { +// _uiState.update { it.copy(error = null) } + } +} + + +sealed class HomeEvent { + data class ShowError(val message: String) : HomeEvent() + data class ShowDetailDrug(val drugDetail:Result?): HomeEvent() +} + +data class CombineState( + val drugSearchState: DrugSearchState = DrugSearchState() , + val drugDetailState: DrugDetailState =DrugDetailState(), + val darmanState: GetDarmaniState = GetDarmaniState(), + val showDarmanList: Boolean = true + ) + +data class DrugSearchState( + val isLoading: Boolean = false, + val drugsData: Result? = null, + val drugDetail: Result? = null, + val error:String? = null +) + +data class DrugDetailState( + val isLoading: Boolean = false, + val drugDetail:Result? = null, + val isDetailVisible: Boolean = false, + val error:String? = null +) + +data class GetDarmaniState( + val isLoading: Boolean = false, + val getDarmani:Result? = null, + val error:String? = null +) + diff --git a/app/src/main/java/com/approagency/drug/presentation/viewModel/LabViewModel.kt b/app/src/main/java/com/approagency/drug/presentation/viewModel/LabViewModel.kt index 0e1cece..07a01ce 100644 --- a/app/src/main/java/com/approagency/drug/presentation/viewModel/LabViewModel.kt +++ b/app/src/main/java/com/approagency/drug/presentation/viewModel/LabViewModel.kt @@ -1,94 +1,174 @@ -package com.approagency.drug.presentation.viewModel - -import android.net.http.HttpException -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.approagency.drug.data.dto.DrugListResponse -import com.approagency.drug.data.dto.DrugModels -import com.approagency.drug.domain.model.TestGroup -import com.approagency.drug.domain.model.TestItem -import com.approagency.drug.domain.usecase.GetTestGroupUseCase -import com.approagency.drug.domain.usecase.GetTestItemByGroupId -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch - -class LabViewModel ( - private val getTestGroupUseCase: GetTestGroupUseCase, - private val getTestItemByGroupId: GetTestItemByGroupId -) : ViewModel(){ - private val _testGroups = MutableStateFlow(TestGroupItem()) - val testGroups: StateFlow = _testGroups.asStateFlow() - - private val _testItems = MutableStateFlow(TestItemState()) - val testItems: StateFlow = _testItems.asStateFlow() - - init { - loadTestGroups() - } - - fun loadTestGroups(){ - _testGroups.update { - it.copy( - isLoading = true - ) - } - viewModelScope.launch { - try { - val result = getTestGroupUseCase.invoke(); - _testGroups.update { - it.copy( - isLoading = false, - testGroup = result, - ) - } - } catch (e:HttpException){ - handleError(e.message) - } - - } - } - - fun getItemByGroupId(id:Int){ - _testItems.update { it.copy(isLoading = true) } - viewModelScope.launch { - try { - val result = getTestItemByGroupId.invoke(id); - _testItems.update { - it.copy( - isLoading = false, - testItem = result - ) - } - } catch (e:HttpException){ - handleError(e.message) - } - } - } - private suspend fun handleError(message: String?) { - _testGroups.update { - it.copy( - isLoading = false, - testGroup = null, - error = message - ) - } - } - -} - -data class TestGroupItem( - val isLoading: Boolean = false, - val testGroup: Flow>? = null, - val error:String? = null -) - -data class TestItemState( - val isLoading: Boolean = false, - val testItem: Flow>? = null, - val error:String? = null -) - +package com.approagency.drug.presentation.viewModel + +import android.net.http.HttpException +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.approagency.drug.data.dto.DrugListResponse +import com.approagency.drug.data.dto.DrugModels +import com.approagency.drug.domain.model.TestGroup +import com.approagency.drug.domain.model.TestItem +import com.approagency.drug.domain.usecase.GetTestGroupUseCase +import com.approagency.drug.domain.usecase.GetTestItemByGroupId +import com.approagency.drug.domain.usecase.SearchTestsUseCase +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.debounce +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class LabViewModel ( + private val getTestGroupUseCase: GetTestGroupUseCase, + private val getTestItemByGroupId: GetTestItemByGroupId, + private val searchTestsUseCase: SearchTestsUseCase +) : ViewModel(){ + private val _testGroups = MutableStateFlow(TestGroupItem()) + val testGroups: StateFlow = _testGroups.asStateFlow() + + private val _testItems = MutableStateFlow(TestItemState()) + val testItems: StateFlow = _testItems.asStateFlow() + + + private val _searchResults = MutableStateFlow(SearchResultState()) + val searchResults: StateFlow = _searchResults.asStateFlow() + + private val searchQuery = MutableStateFlow("") + + init { + loadTestGroups() + setupSearch() + } + private fun setupSearch() { + viewModelScope.launch { + searchQuery + .debounce(1000) // Wait 500ms after user stops typing + .distinctUntilChanged() + .collect { query -> + if (query.isNotBlank()) { + performSearch(query) + } else { + _searchResults.update { + SearchResultState( + isLoading = false, + searchResults = null, + query = "" + ) + } + } + } + } + } + + fun updateSearchQuery(query: String) { + searchQuery.value = query + _searchResults.update { + it.copy( + isLoading = query.isNotBlank(), + query = query + ) + } + } + + private fun performSearch(query: String) { + viewModelScope.launch { + try { + val result = searchTestsUseCase.invoke(query) + result.collect { (groups, items) -> + _searchResults.update { + it.copy( + isLoading = false, + searchResults = SearchResult(groups = groups, items = items), + error = null + ) + } + } + } catch (e: Exception) { + _searchResults.update { + it.copy( + isLoading = false, + searchResults = 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>? = null, + val error:String? = null +) + +data class TestItemState( + val isLoading: Boolean = false, + val testItem: Flow>? = 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, + val items: List +) diff --git a/app/src/main/java/com/approagency/drug/ui/theme/Dimention.kt b/app/src/main/java/com/approagency/drug/ui/theme/Dimention.kt index a4ee43a..b3a56c9 100644 --- a/app/src/main/java/com/approagency/drug/ui/theme/Dimention.kt +++ b/app/src/main/java/com/approagency/drug/ui/theme/Dimention.kt @@ -1,20 +1,20 @@ -package com.vada.caller.ui.theme - -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp - - -data class Dime ( - val xxs: Dp = 2.dp, - val xs: Dp = 4.dp, - val sm: Dp = 8.dp, - val md: Dp = 12.dp, - val lg: Dp = 16.dp, - val xl: Dp = 20.dp, - val xxl: Dp = 24.dp, - - - val iconSm: Dp = 20.dp, - val iconMd: Dp = 24.dp, - val iconLg: Dp = 32.dp, +package com.vada.caller.ui.theme + +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + + +data class Dime ( + val xxs: Dp = 2.dp, + val xs: Dp = 4.dp, + val sm: Dp = 8.dp, + val md: Dp = 12.dp, + val lg: Dp = 16.dp, + val xl: Dp = 20.dp, + val xxl: Dp = 24.dp, + + + val iconSm: Dp = 20.dp, + val iconMd: Dp = 24.dp, + val iconLg: Dp = 32.dp, ) \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/ui/theme/Local.kt b/app/src/main/java/com/approagency/drug/ui/theme/Local.kt index d64e615..d76427e 100644 --- a/app/src/main/java/com/approagency/drug/ui/theme/Local.kt +++ b/app/src/main/java/com/approagency/drug/ui/theme/Local.kt @@ -1,5 +1,5 @@ -package com.vada.caller.ui.theme - -import androidx.compose.runtime.staticCompositionLocalOf - +package com.vada.caller.ui.theme + +import androidx.compose.runtime.staticCompositionLocalOf + val LocalDime = staticCompositionLocalOf { Dime() } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/ui/theme/MaterialThemeExt.kt b/app/src/main/java/com/approagency/drug/ui/theme/MaterialThemeExt.kt index dc456c5..03c2081 100644 --- a/app/src/main/java/com/approagency/drug/ui/theme/MaterialThemeExt.kt +++ b/app/src/main/java/com/approagency/drug/ui/theme/MaterialThemeExt.kt @@ -1,8 +1,8 @@ -package com.vada.caller.ui.theme - -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable - -val MaterialTheme.dime: Dime -@Composable +package com.vada.caller.ui.theme + +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable + +val MaterialTheme.dime: Dime +@Composable get() =LocalDime.current \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/ui/theme/Shape.kt b/app/src/main/java/com/approagency/drug/ui/theme/Shape.kt index f2f4db7..c32eb4c 100644 --- a/app/src/main/java/com/approagency/drug/ui/theme/Shape.kt +++ b/app/src/main/java/com/approagency/drug/ui/theme/Shape.kt @@ -1,12 +1,12 @@ -package com.fabirt.podcastapp.ui.theme - -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Shapes -import androidx.compose.ui.unit.dp - -val Shapes = Shapes( - small = RoundedCornerShape(4.dp), - medium = RoundedCornerShape(12.dp), - large = RoundedCornerShape(16.dp), - extraLarge = RoundedCornerShape(20.dp) +package com.fabirt.podcastapp.ui.theme + +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Shapes +import androidx.compose.ui.unit.dp + +val Shapes = Shapes( + small = RoundedCornerShape(4.dp), + medium = RoundedCornerShape(12.dp), + large = RoundedCornerShape(16.dp), + extraLarge = RoundedCornerShape(20.dp) ) \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/utils/Config.kt b/app/src/main/java/com/approagency/drug/utils/Config.kt index f8e3b0a..4a4d950 100644 --- a/app/src/main/java/com/approagency/drug/utils/Config.kt +++ b/app/src/main/java/com/approagency/drug/utils/Config.kt @@ -1,5 +1,5 @@ -package com.approagency.drug.utils - -object Config { - const val BASE_URL = "https://drug.approagency.ir/api/" +package com.approagency.drug.utils + +object Config { + const val BASE_URL = "https://drug.approagency.ir/api/" } \ No newline at end of file diff --git a/app/src/main/java/com/approagency/drug/utils/retry.kt b/app/src/main/java/com/approagency/drug/utils/retry.kt new file mode 100644 index 0000000..d55015f --- /dev/null +++ b/app/src/main/java/com/approagency/drug/utils/retry.kt @@ -0,0 +1,29 @@ +package com.approagency.drug.utils + +import kotlinx.coroutines.delay + +suspend fun 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 { + 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) + } +} diff --git a/app/src/main/res/font/aldrich.xml b/app/src/main/res/font/aldrich.xml index 537af3e..c906c6a 100644 --- a/app/src/main/res/font/aldrich.xml +++ b/app/src/main/res/font/aldrich.xml @@ -1,7 +1,7 @@ - - - + + + diff --git a/app/src/main/res/values/font_certs.xml b/app/src/main/res/values/font_certs.xml index d2226ac..54895a3 100644 --- a/app/src/main/res/values/font_certs.xml +++ b/app/src/main/res/values/font_certs.xml @@ -1,17 +1,17 @@ - - - - @array/com_google_android_gms_fonts_certs_dev - @array/com_google_android_gms_fonts_certs_prod - - - - MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= - - - - - MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK - - - + + + + @array/com_google_android_gms_fonts_certs_dev + @array/com_google_android_gms_fonts_certs_prod + + + + MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs= + + + + + MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK + + + diff --git a/app/src/main/res/values/preloaded_fonts.xml b/app/src/main/res/values/preloaded_fonts.xml index de54401..c57b596 100644 --- a/app/src/main/res/values/preloaded_fonts.xml +++ b/app/src/main/res/values/preloaded_fonts.xml @@ -1,6 +1,6 @@ - - - - @font/aldrich - - + + + + @font/aldrich + + diff --git a/gradle.properties b/gradle.properties index 940c3df..c849bb7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,4 +21,10 @@ kotlin.code.style=official # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true -kotlin.compiler.execution.strategy=in-process \ No newline at end of file +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 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1aa1982..7ce0d9c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,4 +1,3 @@ -#Thu Feb 26 19:18:21 IRST 2026 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://maven.myket.ir/gradle/distributions/gradle-8.13-bin.zip diff --git a/gradlew.bat b/gradlew.bat index db3a6ac..5eed7ee 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,94 +1,94 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@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 obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@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 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -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. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 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 Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 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 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH= - - -@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" %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@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 obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@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 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +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. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 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 Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 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 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@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" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts index 5e7abcc..c41b3c2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,19 +1,19 @@ pluginManagement { repositories { maven { url = uri("https://maven.myket.ir") } - google { - content { - includeGroupByRegex("com\\.android.*") - includeGroupByRegex("com\\.google.*") - includeGroupByRegex("androidx.*") - } - } +// google { +// content { +// includeGroupByRegex("com\\.android.*") +// includeGroupByRegex("com\\.google.*") +// includeGroupByRegex("androidx.*") +// } +// } // mavenCentral() // gradlePluginPortal() } } dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositoriesMode.set(RepositoriesMode.PREFER_PROJECT) repositories { //google() //mavenCentral()