diff --git a/.github/workflows/opencommit.yml b/.github/workflows/opencommit.yml deleted file mode 100644 index 2ab5adcc..00000000 --- a/.github/workflows/opencommit.yml +++ /dev/null @@ -1,39 +0,0 @@ -# name: 'OpenCommit Action' - -# on: -# push: -# # this list of branches is often enough, -# # but you may still ignore other public branches -# branches-ignore: [main master dev development release] - -# jobs: -# opencommit: -# timeout-minutes: 20 -# name: OpenCommit -# runs-on: ubuntu-latest -# permissions: write-all -# steps: -# - name: Setup Node.js Environment -# uses: actions/setup-node@v3 -# with: -# node-version: '16' -# - uses: actions/checkout@v3 -# with: -# fetch-depth: 0 -# - uses: di-sukharev/opencommit@github-action-v1.0.4 -# with: -# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - -# env: -# # set openAI api key in repo actions secrets, -# # for openAI keys go to: https://platform.openai.com/account/api-keys -# # for repo secret go to: /settings/secrets/actions -# OCO_OPENAI_API_KEY: ${{ secrets.OCO_OPENAI_API_KEY }} - -# # customization -# OCO_OPENAI_MAX_TOKENS: 500 -# OCO_OPENAI_BASE_PATH: '' -# OCO_DESCRIPTION: false -# OCO_EMOJI: true -# OCO_MODEL: gpt-3.5-turbo -# OCO_LANGUAGE: en diff --git a/app/build.gradle.kts b/app/build.gradle.kts index be12d93c..24bc823b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,12 +1,14 @@ +import com.niyaj.samples.apps.popos.PoposBuildType + @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed plugins { - alias(libs.plugins.android.application) - alias(libs.plugins.kotlin.android) - alias(libs.plugins.hilt) + id("popos.android.application") + id("popos.android.application.flavors") + id("popos.android.application.jacoco") + id("popos.android.hilt") + id("jacoco") alias(libs.plugins.appsweep) - alias(libs.plugins.androidx.baselineprofile) alias(libs.plugins.ksp) - id(libs.plugins.kotlin.kapt.get().pluginId) } android { @@ -19,226 +21,154 @@ android { targetSdk = libs.versions.targetSdk.get().toInt() versionCode = libs.versions.versionCode.get().toInt() versionName = libs.versions.versionName.get() - testInstrumentationRunner = "com.niyaj.popos.HiltTestRunner" + testInstrumentationRunner = "com.niyaj.testing.PoposTestRunner" manifestPlaceholders.putAll(mapOf("sentryEnvironment" to "production")) - vectorDrawables { - useSupportLibrary = true - } - - ksp { - arg("room.schemaLocation", "$projectDir/schemas") - } } - - buildTypes { - release { + val debug by getting { + applicationIdSuffix = PoposBuildType.DEBUG.applicationIdSuffix + } + val release by getting { + isMinifyEnabled = true + applicationIdSuffix = PoposBuildType.RELEASE.applicationIdSuffix + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + + // To publish on the Play store a private signing key is required, but to allow anyone + // who clones the code to sign and run the release variant, use the debug signing key. + // TODO: Abstract the signing configuration to a separate file to avoid hardcoding this. + signingConfig = signingConfigs.getByName("debug") + } + val benchmark by creating { + // Enable all the optimizations from release build through initWith(release). + initWith(release) + matchingFallbacks.add("release") + // Debug key signing is available to everyone. + signingConfig = signingConfigs.getByName("debug") + // Only use benchmark proguard rules + proguardFiles("benchmark-rules.pro") isMinifyEnabled = true - isShrinkResources = true - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) + applicationIdSuffix = PoposBuildType.BENCHMARK.applicationIdSuffix } } + compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } + kotlinOptions { - jvmTarget = "17" + jvmTarget = "11" } + buildFeatures { compose = true buildConfig = true } + composeOptions { - kotlinCompilerExtensionVersion = "1.4.4" + kotlinCompilerExtensionVersion = libs.versions.androidxComposeCompiler.get() } + packaging { resources { - excludes += "/META-INF/{AL2.0,LGPL2.1}" - excludes += "META-INF/LICENSE.md" - excludes += "META-INF/LICENSE-notice.md" - excludes += "DebugProbesKt.bin" + excludes.add("/META-INF/{AL2.0,LGPL2.1}") + excludes.add("META-INF/LICENSE.md") + excludes.add("META-INF/LICENSE-notice.md") + excludes.add("DebugProbesKt.bin") } } - sourceSets { - getByName("main") { - java.srcDir("src/main/kotlin") - } - getByName("test") { - java.srcDir("src/test/kotlin") - } - getByName("androidTest") { - java.srcDir("src/androidTest/kotlin") + testOptions { + unitTests { + isIncludeAndroidResources = true } } - hilt { - enableAggregatingTask = true + appsweep { + apiKey = "gs_appsweep_9LOhFix_p6bow4wYcoXwSfLLe6U7ZPDT5HDoGHxw" } } dependencies { - implementation(libs.core.ktx) - -// implementation(platform(libs.compose.bom)) - implementation(libs.ui) - implementation(libs.ui.graphics) -// implementation(libs.material) - implementation(libs.material3) - implementation(libs.material.icons) - implementation(libs.ui.tooling.preview) - implementation(libs.material3.window.size) - implementation(libs.activity.compose) - - //Startup & Splash screen - implementation(libs.startup) - implementation(libs.splashscreen) - - implementation(libs.runtime.livedata) - - // ViewModel - implementation(libs.viewmodel.ktx) - implementation(libs.viewmodel.compose) - implementation(libs.runtime.compose) - implementation(libs.runtime.ktx) - // Saved state module for ViewModel - implementation(libs.viewmodel.savedstate) - // Annotation processor - implementation(libs.common.java8) - - // Compose dependencies - implementation(libs.navigation.compose) - - // Kotlin + coroutines - implementation(libs.work.runtime.ktx) - androidTestImplementation(libs.work.testing) - - //Accompanist -// implementation(libs.flowlayout) - implementation(libs.systemuicontroller) - implementation(libs.permissions) -// implementation(libs.swiperefresh) -// implementation(libs.placeholder.material) -// implementation(libs.pager) -// implementation(libs.pager.indicators) - - // Coroutines - implementation(libs.coroutines.core) - implementation(libs.coroutines.android) - - // For testing - testImplementation(libs.coroutines.test) - androidTestImplementation(libs.coroutines.test) - - //Hilt Work - implementation(libs.hilt.work) - kapt(libs.hilt.compiler) - - implementation(libs.hilt.navigation.compose) - kapt(libs.hilt.android) - - // Dagger & Hilt - implementation(libs.hilt.android) - kapt(libs.hilt.dagger.compiler) - - // For testing - androidTestImplementation(libs.hilt.android.testing) - kaptAndroidTest(libs.hilt.dagger.compiler) - - testImplementation(libs.hilt.android.testing) - kaptTest(libs.hilt.dagger.compiler) - - // Timber - implementation(libs.timber) - - //RevealSwipe - implementation(libs.revealswipe) - - //Pos.printer - implementation(libs.pos.printer) - - //Realm - // implementation(libs.realm.library.base) - - // Room - implementation(libs.room.runtime) - ksp(libs.room.complier) - - // To use Kotlin annotation processing tool (kapt) - ksp(libs.room.complier) - - // optional - Kotlin Extensions and Coroutines support for Room - implementation(libs.room.ktx) - - // optional - Test helpers - testImplementation(libs.room.testing) - // optional - Paging 3 Integration - implementation(libs.room.paging) - - // Paging - implementation(libs.paging.runtime) - // optional - Jetpack Compose integration - implementation(libs.paging.compose) - - // debugImplementation because LeakCanary should only run in debug builds. - debugImplementation(libs.leakcanary) + implementation(project(":feature:account")) + implementation(project(":feature:addonitem")) + implementation(project(":feature:address")) + implementation(project(":feature:cart")) + implementation(project(":feature:cart_selected")) + implementation(project(":feature:cartorder")) + implementation(project(":feature:category")) + implementation(project(":feature:charges")) + implementation(project(":feature:customer")) + implementation(project(":feature:employee")) + implementation(project(":feature:employee_payment")) + implementation(project(":feature:employee_absent")) + implementation(project(":feature:expenses")) + implementation(project(":feature:home")) + implementation(project(":feature:order")) + implementation(project(":feature:print_order")) + implementation(project(":feature:product")) + implementation(project(":feature:profile")) + implementation(project(":feature:settings")) + implementation(project(":feature:printer_info")) + + + implementation(project(":core:common")) + implementation(project(":core:ui")) + implementation(project(":core:designsystem")) + implementation(project(":core:data")) + implementation(project(":core:model")) + + androidTestImplementation(project(":core:testing")) + androidTestImplementation(libs.androidx.navigation.testing) + androidTestImplementation(libs.accompanist.testharness) + androidTestImplementation(kotlin("test")) + debugImplementation(libs.androidx.compose.ui.testManifest) +// debugImplementation(project(":ui-test-hilt-manifest")) + + implementation(libs.accompanist.systemuicontroller) + implementation(libs.accompanist.permissions) + + implementation(libs.androidx.activity.compose) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.core.splashscreen) + implementation(libs.androidx.core.startup) + implementation(libs.androidx.compose.runtime) + implementation(libs.androidx.lifecycle.runtimeCompose) + implementation(libs.androidx.compose.material3.windowSizeClass) + implementation(libs.androidx.lifecycle.livedata.ktx) + implementation(libs.androidx.compose.runtime.tracing) + implementation(libs.androidx.hilt.navigation.compose) + implementation(libs.androidx.navigation.compose) + implementation(libs.androidx.window.manager) + implementation(libs.androidx.profileinstaller) + implementation(libs.kotlinx.coroutines.guava) //RaamCosta Library - implementation(libs.raamcosta.core) + implementation(libs.raamcosta.animation.core) ksp(libs.raamcosta.ksp) - //Moshi - ksp(libs.moshi.kotlin.codegen) - implementation(libs.moshi) - - //ProfileInstaller - implementation(libs.profileinstaller) - - // Local unit tests - testImplementation(libs.junit) - androidTestImplementation(libs.junit) - - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.espresso.core) - testImplementation(libs.androidx.test.core) - androidTestImplementation(libs.androidx.test.core.ktx) - androidTestImplementation(libs.androidx.test.runner) - testImplementation(libs.androidx.arch.core.testing) - androidTestImplementation(libs.androidx.arch.core.testing) - - androidTestImplementation(libs.ui.test.junit) - debugImplementation(libs.ui.tooling) - debugImplementation(libs.ui.test.manifest) - - // Truth - testImplementation(libs.truth) - androidTestImplementation(libs.truth) + // Timber + implementation(libs.timber) - //Mockk - androidTestImplementation(libs.mockk.android) - testImplementation(libs.mockk) + //Google Play Play Integrity API + implementation(libs.play.integrity) - //ACRA Logger - implementation(libs.acra.mail) - implementation(libs.acra.toast) + // Google Play In-App Updates API + implementation(libs.play.app.update) + implementation(libs.play.app.update.ktx) - //Sentry - implementation(libs.sentry.android) - implementation(libs.sentry.compose.android) + // zxing QR code library + implementation(libs.zxing.core) - // Vanpara DatePicker - implementation(libs.dialog.datetime) - implementation(libs.navigation.bar) + // Play GMS Scanner library + implementation(libs.play.gms.scanner) - //Baseline Profile -// "baselineProfile"(project(mapOf("path" to ":benchmark"))) + // Play Service Base + implementation(libs.play.service) } kapt { diff --git a/app/schemas/com.niyaj.poposroom.features.common.database.PoposDatabase/1.json b/app/schemas/com.niyaj.poposroom.features.common.database.PoposDatabase/1.json index ab2d80c9..0dfc6762 100644 --- a/app/schemas/com.niyaj.poposroom.features.common.database.PoposDatabase/1.json +++ b/app/schemas/com.niyaj.poposroom.features.common.database.PoposDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "496db8e04a7bf378d7cd583a90d6451f", + "identityHash": "a8393431475bf855d7b354dbcdca75a7", "entities": [ { "tableName": "addonitem", @@ -1275,12 +1275,104 @@ ] } ] + }, + { + "tableName": "Profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`restaurantId` INTEGER NOT NULL, `name` TEXT NOT NULL, `email` TEXT NOT NULL, `primaryPhone` TEXT NOT NULL, `secondaryPhone` TEXT NOT NULL, `tagline` TEXT NOT NULL, `description` TEXT NOT NULL, `address` TEXT NOT NULL, `logo` TEXT NOT NULL, `printLogo` TEXT NOT NULL, `paymentQrCode` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT, PRIMARY KEY(`restaurantId`))", + "fields": [ + { + "fieldPath": "restaurantId", + "columnName": "restaurantId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "primaryPhone", + "columnName": "primaryPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondaryPhone", + "columnName": "secondaryPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tagline", + "columnName": "tagline", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "logo", + "columnName": "logo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "printLogo", + "columnName": "printLogo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentQrCode", + "columnName": "paymentQrCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "restaurantId" + ] + }, + "indices": [], + "foreignKeys": [] } ], "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '496db8e04a7bf378d7cd583a90d6451f')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a8393431475bf855d7b354dbcdca75a7')" ] } } \ No newline at end of file diff --git a/app/schemas/com.niyaj.poposroom.features.common.database.PoposDatabase/5.json b/app/schemas/com.niyaj.poposroom.features.common.database.PoposDatabase/5.json new file mode 100644 index 00000000..44cd1541 --- /dev/null +++ b/app/schemas/com.niyaj.poposroom.features.common.database.PoposDatabase/5.json @@ -0,0 +1,1192 @@ +{ + "formatVersion": 1, + "database": { + "version": 5, + "identityHash": "342fa758e13d0f3f2c53272c2943e966", + "entities": [ + { + "tableName": "addonitem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `itemName` TEXT NOT NULL, `itemPrice` INTEGER NOT NULL, `isApplicable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemName", + "columnName": "itemName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "itemPrice", + "columnName": "itemPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isApplicable", + "columnName": "isApplicable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_addonitem_itemId", + "unique": false, + "columnNames": [ + "itemId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_addonitem_itemId` ON `${TABLE_NAME}` (`itemId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "address", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`addressId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addressName` TEXT NOT NULL, `shortName` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "addressId", + "columnName": "addressId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "addressName", + "columnName": "addressName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "shortName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "addressId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "charges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`chargesId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `chargesName` TEXT NOT NULL, `chargesPrice` INTEGER NOT NULL, `isApplicable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "chargesId", + "columnName": "chargesId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chargesName", + "columnName": "chargesName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chargesPrice", + "columnName": "chargesPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isApplicable", + "columnName": "isApplicable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "chargesId" + ] + }, + "indices": [ + { + "name": "index_charges_chargesId", + "unique": false, + "columnNames": [ + "chargesId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_charges_chargesId` ON `${TABLE_NAME}` (`chargesId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "category", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `categoryName` TEXT NOT NULL, `isAvailable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryName", + "columnName": "categoryName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isAvailable", + "columnName": "isAvailable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "categoryId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "customer", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`customerId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `customerName` TEXT, `customerPhone` TEXT NOT NULL, `customerEmail` TEXT, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "customerId", + "columnName": "customerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customerName", + "columnName": "customerName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customerPhone", + "columnName": "customerPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "customerEmail", + "columnName": "customerEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "customerId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "employee", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`employeeId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `employeeName` TEXT NOT NULL, `employeePhone` TEXT NOT NULL, `employeeSalary` TEXT NOT NULL, `employeePosition` TEXT NOT NULL, `employeeJoinedDate` TEXT NOT NULL, `employeeEmail` TEXT, `employeeSalaryType` TEXT NOT NULL, `employeeType` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "employeeName", + "columnName": "employeeName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeePhone", + "columnName": "employeePhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeSalary", + "columnName": "employeeSalary", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeePosition", + "columnName": "employeePosition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeJoinedDate", + "columnName": "employeeJoinedDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeEmail", + "columnName": "employeeEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "employeeSalaryType", + "columnName": "employeeSalaryType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeType", + "columnName": "employeeType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "employeeId" + ] + }, + "indices": [ + { + "name": "index_employee_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_employee_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "payment", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`paymentId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `employeeId` INTEGER NOT NULL, `paymentAmount` TEXT NOT NULL, `paymentDate` TEXT NOT NULL, `paymentType` TEXT NOT NULL, `paymentMode` TEXT NOT NULL, `paymentNote` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL, FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "paymentId", + "columnName": "paymentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "paymentAmount", + "columnName": "paymentAmount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentDate", + "columnName": "paymentDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentType", + "columnName": "paymentType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentMode", + "columnName": "paymentMode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentNote", + "columnName": "paymentNote", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "paymentId" + ] + }, + "indices": [ + { + "name": "index_payment_paymentId", + "unique": false, + "columnNames": [ + "paymentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_payment_paymentId` ON `${TABLE_NAME}` (`paymentId`)" + }, + { + "name": "index_payment_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_payment_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + } + ] + }, + { + "tableName": "EmployeeWithPaymentCrossRef", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`employeeId` INTEGER NOT NULL, `paymentId` INTEGER NOT NULL, PRIMARY KEY(`employeeId`, `paymentId`), FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`paymentId`) REFERENCES `payment`(`paymentId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "paymentId", + "columnName": "paymentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "employeeId", + "paymentId" + ] + }, + "indices": [ + { + "name": "index_EmployeeWithPaymentCrossRef_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithPaymentCrossRef_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + }, + { + "name": "index_EmployeeWithPaymentCrossRef_paymentId", + "unique": false, + "columnNames": [ + "paymentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithPaymentCrossRef_paymentId` ON `${TABLE_NAME}` (`paymentId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + }, + { + "table": "payment", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "paymentId" + ], + "referencedColumns": [ + "paymentId" + ] + } + ] + }, + { + "tableName": "absent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`absentId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `employeeId` INTEGER NOT NULL, `absentReason` TEXT NOT NULL, `absentDate` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL, FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "absentId", + "columnName": "absentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absentReason", + "columnName": "absentReason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absentDate", + "columnName": "absentDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "absentId" + ] + }, + "indices": [ + { + "name": "index_absent_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_absent_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + } + ] + }, + { + "tableName": "EmployeeWithAbsentCrossRef", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`employeeId` INTEGER NOT NULL, `absentId` INTEGER NOT NULL, PRIMARY KEY(`employeeId`, `absentId`), FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`absentId`) REFERENCES `absent`(`absentId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absentId", + "columnName": "absentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "employeeId", + "absentId" + ] + }, + "indices": [ + { + "name": "index_EmployeeWithAbsentCrossRef_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithAbsentCrossRef_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + }, + { + "name": "index_EmployeeWithAbsentCrossRef_absentId", + "unique": false, + "columnNames": [ + "absentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithAbsentCrossRef_absentId` ON `${TABLE_NAME}` (`absentId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + }, + { + "table": "absent", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "absentId" + ], + "referencedColumns": [ + "absentId" + ] + } + ] + }, + { + "tableName": "Expense", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`expenseId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `expenseName` TEXT NOT NULL, `expenseAmount` TEXT NOT NULL, `expenseDate` TEXT NOT NULL, `expenseNote` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "expenseId", + "columnName": "expenseId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expenseName", + "columnName": "expenseName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expenseAmount", + "columnName": "expenseAmount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expenseDate", + "columnName": "expenseDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expenseNote", + "columnName": "expenseNote", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "expenseId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "product", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`productId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `categoryId` INTEGER NOT NULL, `productName` TEXT NOT NULL, `productPrice` INTEGER NOT NULL, `productDescription` TEXT NOT NULL, `productAvailability` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL, FOREIGN KEY(`categoryId`) REFERENCES `category`(`categoryId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productName", + "columnName": "productName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "productPrice", + "columnName": "productPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productDescription", + "columnName": "productDescription", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "productAvailability", + "columnName": "productAvailability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "productId" + ] + }, + "indices": [ + { + "name": "index_product_categoryId", + "unique": false, + "columnNames": [ + "categoryId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_product_categoryId` ON `${TABLE_NAME}` (`categoryId`)" + } + ], + "foreignKeys": [ + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "categoryId" + ], + "referencedColumns": [ + "categoryId" + ] + } + ] + }, + { + "tableName": "CategoryWithProductCrossRef", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` INTEGER NOT NULL, `productId` INTEGER NOT NULL, PRIMARY KEY(`productId`, `categoryId`), FOREIGN KEY(`productId`) REFERENCES `product`(`productId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`categoryId`) REFERENCES `category`(`categoryId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "productId", + "categoryId" + ] + }, + "indices": [ + { + "name": "index_CategoryWithProductCrossRef_categoryId", + "unique": false, + "columnNames": [ + "categoryId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CategoryWithProductCrossRef_categoryId` ON `${TABLE_NAME}` (`categoryId`)" + }, + { + "name": "index_CategoryWithProductCrossRef_productId", + "unique": false, + "columnNames": [ + "productId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CategoryWithProductCrossRef_productId` ON `${TABLE_NAME}` (`productId`)" + } + ], + "foreignKeys": [ + { + "table": "product", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "productId" + ], + "referencedColumns": [ + "productId" + ] + }, + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "categoryId" + ], + "referencedColumns": [ + "categoryId" + ] + } + ] + }, + { + "tableName": "cartorder", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cartOrderId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `orderType` TEXT NOT NULL, `orderStatus` TEXT NOT NULL, `doesChargesIncluded` INTEGER NOT NULL, `addressId` INTEGER NOT NULL, `customerId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "cartOrderId", + "columnName": "cartOrderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderType", + "columnName": "orderType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "orderStatus", + "columnName": "orderStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "doesChargesIncluded", + "columnName": "doesChargesIncluded", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "addressId", + "columnName": "addressId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customerId", + "columnName": "customerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "cartOrderId" + ] + }, + "indices": [ + { + "name": "index_cartorder_addressId", + "unique": false, + "columnNames": [ + "addressId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cartorder_addressId` ON `${TABLE_NAME}` (`addressId`)" + }, + { + "name": "index_cartorder_customerId", + "unique": false, + "columnNames": [ + "customerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cartorder_customerId` ON `${TABLE_NAME}` (`customerId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "cart_addon_items", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cartOrderId` INTEGER NOT NULL, `itemId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`cartOrderId`, `itemId`), FOREIGN KEY(`cartOrderId`) REFERENCES `cartorder`(`cartOrderId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`itemId`) REFERENCES `addonitem`(`itemId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "cartOrderId", + "columnName": "cartOrderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "cartOrderId", + "itemId" + ] + }, + "indices": [ + { + "name": "index_cart_addon_items_cartOrderId", + "unique": false, + "columnNames": [ + "cartOrderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_addon_items_cartOrderId` ON `${TABLE_NAME}` (`cartOrderId`)" + }, + { + "name": "index_cart_addon_items_itemId", + "unique": false, + "columnNames": [ + "itemId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_addon_items_itemId` ON `${TABLE_NAME}` (`itemId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "cartOrderId" + ], + "referencedColumns": [ + "cartOrderId" + ] + }, + { + "table": "addonitem", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "itemId" + ], + "referencedColumns": [ + "itemId" + ] + } + ] + }, + { + "tableName": "cart_charges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cartOrderId` INTEGER NOT NULL, `chargesId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`cartOrderId`, `chargesId`), FOREIGN KEY(`cartOrderId`) REFERENCES `cartorder`(`cartOrderId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`chargesId`) REFERENCES `charges`(`chargesId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "cartOrderId", + "columnName": "cartOrderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chargesId", + "columnName": "chargesId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "cartOrderId", + "chargesId" + ] + }, + "indices": [ + { + "name": "index_cart_charges_cartOrderId", + "unique": false, + "columnNames": [ + "cartOrderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_charges_cartOrderId` ON `${TABLE_NAME}` (`cartOrderId`)" + }, + { + "name": "index_cart_charges_chargesId", + "unique": false, + "columnNames": [ + "chargesId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_charges_chargesId` ON `${TABLE_NAME}` (`chargesId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "cartOrderId" + ], + "referencedColumns": [ + "cartOrderId" + ] + }, + { + "table": "charges", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "chargesId" + ], + "referencedColumns": [ + "chargesId" + ] + } + ] + }, + { + "tableName": "selected", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`selectedId` TEXT NOT NULL, `cartOrderId` INTEGER NOT NULL, PRIMARY KEY(`selectedId`), FOREIGN KEY(`cartOrderId`) REFERENCES `cartorder`(`cartOrderId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "selectedId", + "columnName": "selectedId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cartOrderId", + "columnName": "cartOrderId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "selectedId" + ] + }, + "indices": [ + { + "name": "index_selected_selectedId", + "unique": false, + "columnNames": [ + "selectedId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_selected_selectedId` ON `${TABLE_NAME}` (`selectedId`)" + }, + { + "name": "index_selected_cartOrderId", + "unique": false, + "columnNames": [ + "cartOrderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_selected_cartOrderId` ON `${TABLE_NAME}` (`cartOrderId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "cartOrderId" + ], + "referencedColumns": [ + "cartOrderId" + ] + } + ] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '342fa758e13d0f3f2c53272c2943e966')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8b295964..7af00f85 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + @@ -8,30 +7,26 @@ - + - @@ -58,7 +53,7 @@ - + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png index f6bc5794..56bebf0e 100644 Binary files a/app/src/main/ic_launcher-playstore.png and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/niyaj/poposroom/MainActivity.kt b/app/src/main/java/com/niyaj/poposroom/MainActivity.kt new file mode 100644 index 00000000..493bc145 --- /dev/null +++ b/app/src/main/java/com/niyaj/poposroom/MainActivity.kt @@ -0,0 +1,190 @@ +package com.niyaj.poposroom + +import android.os.Bundle +import android.view.WindowManager +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.viewModels +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi +import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.metrics.performance.JankStats +import androidx.profileinstaller.ProfileVerifier +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.niyaj.data.utils.NetworkMonitor +import com.niyaj.designsystem.theme.PoposRoomTheme +import com.niyaj.model.DarkThemeConfig +import com.niyaj.model.ThemeBrand +import com.niyaj.poposroom.ui.PoposApp +import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.guava.await +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import timber.log.Timber +import javax.inject.Inject + +private const val TAG = "MainActivity" + +@AndroidEntryPoint +class MainActivity : ComponentActivity() { + + /** + * Lazily inject [JankStats], which is used to track jank throughout the app. + */ + @Inject + lateinit var lazyStats: dagger.Lazy + + @Inject + lateinit var networkMonitor: NetworkMonitor + + private val viewModel: MainActivityViewModel by viewModels() + + @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + installSplashScreen() + + var uiState: MainActivityUiState by mutableStateOf(MainActivityUiState.Loading) + + // Update the uiState + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.uiState + .onEach { + uiState = it + }.collect() + } + } + + window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) + + // Turn off the decor fitting system windows, which allows us to handle insets, + // including IME animations +// WindowCompat.setDecorFitsSystemWindows(window, false) + + setContent { + val systemUiController = rememberSystemUiController() + val darkTheme = shouldUseDarkTheme(uiState) + + // Update the dark content of the system bars to match the theme + DisposableEffect(systemUiController, darkTheme) { + systemUiController.systemBarsDarkContentEnabled = !darkTheme + onDispose {} + } + + val sizeClass = calculateWindowSizeClass(activity = this) + + PoposRoomTheme( + darkTheme = darkTheme, + androidTheme = shouldUseAndroidTheme(uiState), + disableDynamicTheming = shouldDisableDynamicTheming(uiState), + ) { + PoposApp( + windowSizeClass = sizeClass, + networkMonitor = networkMonitor + ) + } + } + } + + override fun onResume() { + super.onResume() + lazyStats.get().isTrackingEnabled = true + lifecycleScope.launch { + logCompilationStatus() + } + } + + override fun onPause() { + super.onPause() + lazyStats.get().isTrackingEnabled = false + } + + /** + * Logs the app's Baseline Profile Compilation Status using [ProfileVerifier]. + */ + private suspend fun logCompilationStatus() { + /* + When delivering through Google Play, the baseline profile is compiled during installation. + In this case you will see the correct state logged without any further action necessary. + To verify baseline profile installation locally, you need to manually trigger baseline + profile installation. + For immediate compilation, call: + `adb shell cmd package compile -f -m speed-profile com.example.macrobenchmark.target` + You can also trigger background optimizations: + `adb shell pm bg-dexopt-job` + Both jobs run asynchronously and might take some time complete. + To see quick turnaround of the ProfileVerifier, we recommend using `speed-profile`. + If you don't do either of these steps, you might only see the profile status reported as + "enqueued for compilation" when running the sample locally. + */ + withContext(Dispatchers.IO) { + val status = ProfileVerifier.getCompilationStatusAsync().await() + Timber.tag(TAG).d("ProfileInstaller status code: ${status.profileInstallResultCode}") + Timber.d( + TAG, + when { + status.isCompiledWithProfile -> "ProfileInstaller: is compiled with profile" + status.hasProfileEnqueuedForCompilation() -> + "ProfileInstaller: Enqueued for compilation" + + else -> "Profile not compiled or enqueued" + }, + ) + } + } +} + + +/** + * Returns `true` if the Android theme should be used, as a function of the [uiState]. + */ +@Composable +private fun shouldUseAndroidTheme( + uiState: MainActivityUiState, +): Boolean = when (uiState) { + MainActivityUiState.Loading -> false + is MainActivityUiState.Success -> when (uiState.userData.themeBrand) { + ThemeBrand.DEFAULT -> false + ThemeBrand.ANDROID -> true + } +} + +/** + * Returns `true` if the dynamic color is disabled, as a function of the [uiState]. + */ +@Composable +private fun shouldDisableDynamicTheming( + uiState: MainActivityUiState, +): Boolean = when (uiState) { + MainActivityUiState.Loading -> false + is MainActivityUiState.Success -> !uiState.userData.useDynamicColor +} + +/** + * Returns `true` if dark theme should be used, as a function of the [uiState] and the + * current system context. + */ +@Composable +private fun shouldUseDarkTheme( + uiState: MainActivityUiState, +): Boolean = when (uiState) { + MainActivityUiState.Loading -> isSystemInDarkTheme() + is MainActivityUiState.Success -> when (uiState.userData.darkThemeConfig) { + DarkThemeConfig.FOLLOW_SYSTEM -> isSystemInDarkTheme() + DarkThemeConfig.LIGHT -> false + DarkThemeConfig.DARK -> true + } +} diff --git a/app/src/main/java/com/niyaj/poposroom/MainActivityViewModel.kt b/app/src/main/java/com/niyaj/poposroom/MainActivityViewModel.kt new file mode 100644 index 00000000..62149c1f --- /dev/null +++ b/app/src/main/java/com/niyaj/poposroom/MainActivityViewModel.kt @@ -0,0 +1,22 @@ +package com.niyaj.poposroom + +import androidx.lifecycle.ViewModel +import com.niyaj.model.UserData +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class MainActivityViewModel @Inject constructor( +// userDataRepository: UserDataRepository, +) : ViewModel() { + private val _uiState = + MutableStateFlow(MainActivityUiState.Success(UserData())) + val uiState = _uiState.asStateFlow() +} + +sealed interface MainActivityUiState { + data object Loading : MainActivityUiState + data class Success(val userData: UserData) : MainActivityUiState +} diff --git a/app/src/main/java/com/niyaj/poposroom/PoposApplication.kt b/app/src/main/java/com/niyaj/poposroom/PoposApplication.kt index c53a9343..f10d05ac 100644 --- a/app/src/main/java/com/niyaj/poposroom/PoposApplication.kt +++ b/app/src/main/java/com/niyaj/poposroom/PoposApplication.kt @@ -1,17 +1,11 @@ package com.niyaj.poposroom import android.app.Application -import android.content.Context -import android.widget.Toast import dagger.hilt.android.HiltAndroidApp -import org.acra.config.mailSender -import org.acra.config.toast -import org.acra.data.StringFormat -import org.acra.ktx.initAcra import timber.log.Timber @HiltAndroidApp -class PoposApplication: Application() { +class PoposApplication : Application() { override fun onCreate() { super.onCreate() @@ -19,68 +13,4 @@ class PoposApplication: Application() { Timber.plant(Timber.DebugTree()) } } - - override fun attachBaseContext(base: Context) { - super.attachBaseContext(base) - - initAcra { - //core configuration: - stopServicesOnCrash = false - sendReportsInDevMode = false - deleteUnapprovedReportsOnApplicationStart = true - buildConfigClass = BuildConfig::class.java - reportFormat = StringFormat.JSON - - toast { - //required - text = getString(R.string.toast_text) - //defaults to Toast.LENGTH_LONG - length = Toast.LENGTH_LONG - } - - mailSender { - //required - mailTo = "niyaj639@gmail.com" - //defaults to true - reportAsFile = true - //defaults to ACRA-report.stacktrace - reportFileName = "Crash.txt" - //defaults to " Crash Report" - subject = getString(R.string.mail_subject) - //defaults to empty - body = getString(R.string.mail_body) - } - -// notification { -// //required -// title = getString(R.string.notification_title) -// //required -// text = getString(R.string.notification_text) -// //required -// channelName = getString(R.string.notification_channel) -// //optional channel description -// channelDescription = getString(R.string.notification_channel_desc) -// //defaults to NotificationManager.IMPORTANCE_HIGH -//// resChannelImportance = NotificationManager.IMPORTANCE_MAX -// //optional, enables ticker text -//// tickerText = getString(R.string.notification_ticker) -// //defaults to android.R.drawable.stat_sys_warning -// resIcon = R.drawable.ic_clear -// //defaults to android.R.string.ok -// sendButtonText = getString(R.string.notification_send) -// //defaults to android.R.drawable.ic_menu_send -// //defaults to android.R.string.cancel -// discardButtonText = getString(R.string.notification_discard) -// //defaults to android.R.drawable.ic_menu_delete -// //optional, enables inline comment button -//// sendWithCommentButtonText = getString(R.string.notification_send_with_comment) -// //required if above is set -//// resSendWithCommentButtonIcon = R.drawable.notification_send_with_comment -// //optional inline comment hint -//// commentPrompt = getString(R.string.notification_comment) -// //defaults to false -// sendOnClick = true -// } - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/di/JankStatsModule.kt b/app/src/main/java/com/niyaj/poposroom/di/JankStatsModule.kt new file mode 100644 index 00000000..978e34ef --- /dev/null +++ b/app/src/main/java/com/niyaj/poposroom/di/JankStatsModule.kt @@ -0,0 +1,38 @@ +package com.niyaj.poposroom.di + +import android.app.Activity +import android.view.Window +import androidx.metrics.performance.JankStats +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent +import timber.log.Timber + +@Module +@InstallIn(ActivityComponent::class) +object JankStatsModule { + @Provides + fun providesOnFrameListener(): JankStats.OnFrameListener { + return JankStats.OnFrameListener { frameData -> + // Make sure to only log janky frames. + if (frameData.isJank) { + // We're currently logging this but would better report it to a backend. + Timber.tag("NiA Jank").v(frameData.toString()) + } + } + } + + @Provides + fun providesWindow(activity: Activity): Window { + return activity.window + } + + @Provides + fun providesJankStats( + window: Window, + frameListener: JankStats.OnFrameListener, + ): JankStats { + return JankStats.createAndTrack(window, frameListener) + } +} diff --git a/app/src/main/java/com/niyaj/poposroom/features/MainActivity.kt b/app/src/main/java/com/niyaj/poposroom/features/MainActivity.kt deleted file mode 100644 index 58bb3cf2..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/MainActivity.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.niyaj.poposroom.features - -import android.os.Build -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.annotation.RequiresApi -import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi -import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass -import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen -import androidx.core.view.WindowCompat -import dagger.hilt.android.AndroidEntryPoint - -@AndroidEntryPoint -class MainActivity : ComponentActivity() { - @OptIn(ExperimentalMaterial3WindowSizeClassApi::class) - @RequiresApi(Build.VERSION_CODES.R) - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - installSplashScreen() - - // Turn off the decor fitting system windows, which allows us to handle insets, - // including IME animations - WindowCompat.setDecorFitsSystemWindows(window, false) - - setContent { -// val systemUiController: SystemUiController = rememberSystemUiController() -// -// SideEffect { -// systemUiController.isNavigationBarVisible = false -// } - - val sizeClass = calculateWindowSizeClass(activity = this) - - PoposApp(sizeClass) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/PoposApp.kt b/app/src/main/java/com/niyaj/poposroom/features/PoposApp.kt deleted file mode 100644 index 1958ff76..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/PoposApp.kt +++ /dev/null @@ -1,96 +0,0 @@ -package com.niyaj.poposroom.features - -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.imePadding -import androidx.compose.material.rememberScaffoldState -import androidx.compose.material3.BottomSheetScaffold -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Surface -import androidx.compose.material3.rememberBottomSheetScaffoldState -import androidx.compose.material3.rememberModalBottomSheetState -import androidx.compose.material3.windowsizeclass.WindowSizeClass -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.navigation.compose.rememberNavController -import com.niyaj.poposroom.features.common.navigation.PoposNavigation -import com.niyaj.poposroom.features.common.ui.theme.PoposRoomTheme -import com.niyaj.poposroom.features.common.utils.SheetLayout -import com.niyaj.poposroom.features.common.utils.SheetScreen -import com.niyaj.poposroom.features.destinations.MainFeedScreenDestination -import kotlinx.coroutines.launch - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun PoposApp( - sizeClass: WindowSizeClass -) { - // A surface container using the 'background' color from the theme - val scaffoldState = rememberScaffoldState() - val navController = rememberNavController() - val scope = rememberCoroutineScope() - val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false) - val snackbarState = remember { SnackbarHostState() } - val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(bottomSheetState, snackbarState) - - val currentBottomSheet = remember { - mutableStateOf(null) - } - - val closeBottomSheet: () -> Unit = { - scope.launch { - bottomSheetScaffoldState.bottomSheetState.hide() - currentBottomSheet.value = null - } - } - - val openBottomSheet: (SheetScreen) -> Unit = { - scope.launch { - currentBottomSheet.value = it - bottomSheetScaffoldState.bottomSheetState.expand() - } - } - - PoposRoomTheme { - // A surface container using the 'background' color from the theme - Surface( - modifier = Modifier - .fillMaxSize() - .imePadding(), - color = MaterialTheme.colorScheme.background - ) { - BottomSheetScaffold( - modifier = Modifier, - scaffoldState = bottomSheetScaffoldState, - sheetContent = { - currentBottomSheet.value?.let { - SheetLayout( - current = it, - onCloseBottomSheet = closeBottomSheet, - ) - } - }, - sheetSwipeEnabled = true, - sheetPeekHeight = 0.dp, - ) { - PoposNavigation( - bottomSheetScaffoldState = bottomSheetScaffoldState, - scaffoldState = scaffoldState, - snackbarState = snackbarState, - navController = navController, - startRoute = MainFeedScreenDestination, - closeSheet = closeBottomSheet, - onOpenSheet = openBottomSheet - ) - } - } - } -} - - - diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemScreen.kt b/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemScreen.kt deleted file mode 100644 index de2c060d..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemScreen.kt +++ /dev/null @@ -1,142 +0,0 @@ -package com.niyaj.poposroom.features.addon_item.presentation.add_edit - -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.width -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Category -import androidx.compose.material.icons.filled.CurrencyRupee -import androidx.compose.material.icons.filled.Edit -import androidx.compose.material3.Checkbox -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.text.input.KeyboardType -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADDON_APPLIED_SWITCH -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADDON_NAME_ERROR_TAG -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADDON_NAME_FIELD -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADDON_PRICE_ERROR_TAG -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADDON_PRICE_FIELD -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADD_EDIT_ADDON_SCREEN -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.CREATE_NEW_ADD_ON -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.EDIT_ADD_ON_ITEM -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardTextField -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.safeString - -@Composable -fun AddEditItemScreen( - addOnItemId: Int = 0, - closeSheet: () -> Unit, - viewModel: AddEditAddOnItemViewModel = hiltViewModel(), -) { - val nameError = viewModel.nameError.collectAsStateWithLifecycle().value - val priceError = viewModel.priceError.collectAsStateWithLifecycle().value - - val enableBtn = nameError == null && priceError == null - - val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value - - LaunchedEffect(key1 = Unit) { - viewModel.resetFields() - } - - LaunchedEffect(key1 = event) { - event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} - is UiEvent.OnError -> { - closeSheet() - } - is UiEvent.OnSuccess -> { - closeSheet() - } - } - } - } - - LaunchedEffect(key1 = addOnItemId) { - viewModel.getAllAddOnItemById(addOnItemId) - } - - Column( - modifier = Modifier - .testTag(ADD_EDIT_ADDON_SCREEN) - .fillMaxWidth(), - verticalArrangement = Arrangement.Center, - ) { - StandardTextField( - value = viewModel.addEditState.itemName, - label = ADDON_NAME_FIELD, - leadingIcon = Icons.Default.Category, - isError = nameError != null, - errorText = nameError, - errorTextTag = ADDON_NAME_ERROR_TAG, - onValueChange = { - viewModel.onEvent(AddEditAddOnItemEvent.ItemNameChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardTextField( - value = viewModel.addEditState.itemPrice.safeString, - label = ADDON_PRICE_FIELD, - leadingIcon = Icons.Default.CurrencyRupee, - isError = priceError != null, - errorText = priceError, - keyboardType = KeyboardType.Number, - errorTextTag = ADDON_PRICE_ERROR_TAG, - onValueChange = { - viewModel.onEvent(AddEditAddOnItemEvent.ItemPriceChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Checkbox( - modifier = Modifier.testTag(ADDON_APPLIED_SWITCH), - checked = viewModel.addEditState.isApplicable, - onCheckedChange = { - viewModel.onEvent(AddEditAddOnItemEvent.ItemApplicableChanged) - } - ) - Spacer(modifier = Modifier.width(SpaceSmall)) - Text( - text = if(viewModel.addEditState.isApplicable) - "Marked as applied" - else - "Marked as not applied", - style = MaterialTheme.typography.labelMedium - ) - } - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardButton( - text = if (addOnItemId == 0) CREATE_NEW_ADD_ON else EDIT_ADD_ON_ITEM, - enabled = enableBtn, - icon = if (addOnItemId == 0) Icons.Default.Add else Icons.Default.Edit, - onClick = { - viewModel.onEvent(AddEditAddOnItemEvent.CreateUpdateAddOnItem(addOnItemId)) - } - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressScreen.kt b/app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressScreen.kt deleted file mode 100644 index 239870f7..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressScreen.kt +++ /dev/null @@ -1,113 +0,0 @@ -package com.niyaj.poposroom.features.address.presentation.add_edit - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Business -import androidx.compose.material.icons.filled.CurrencyRupee -import androidx.compose.material.icons.filled.EditLocationAlt -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.ADDRESS_FULL_NAME_ERROR -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.ADDRESS_FULL_NAME_FIELD -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.ADDRESS_SHORT_NAME_ERROR -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.ADDRESS_SHORT_NAME_FIELD -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.CREATE_ADDRESS_SCREEN -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.CREATE_NEW_ADDRESS -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.EDIT_ADDRESS -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.UPDATE_ADDRESS_SCREEN -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardTextField -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent - -@Composable -fun AddEditAddressScreen( - addressId: Int = 0, - closeSheet: () -> Unit, - viewModel: AddEditAddressViewModel = hiltViewModel(), -) { - - val nameError = viewModel.nameError.collectAsStateWithLifecycle().value - val shortNameError = viewModel.shortNameError.collectAsStateWithLifecycle().value - - val enableBtn = nameError == null && shortNameError == null - - val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value - - LaunchedEffect(key1 = Unit) { - viewModel.resetFields() - } - - LaunchedEffect(key1 = event) { - event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} - is UiEvent.OnError -> { - closeSheet() - } - is UiEvent.OnSuccess -> { - closeSheet() - } - } - } - } - - LaunchedEffect(key1 = addressId) { - viewModel.getAddressById(addressId) - } - - val testTag = if (addressId == 0) CREATE_ADDRESS_SCREEN else UPDATE_ADDRESS_SCREEN - - Column( - modifier = Modifier - .testTag(testTag) - .fillMaxWidth(), - verticalArrangement = Arrangement.Center, - ) { - StandardTextField( - value = viewModel.state.addressName, - label = ADDRESS_FULL_NAME_FIELD, - leadingIcon = Icons.Default.Business, - isError = nameError != null, - errorText = nameError, - errorTextTag = ADDRESS_FULL_NAME_ERROR, - onValueChange = { - viewModel.onEvent(AddEditAddressEvent.AddressNameChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardTextField( - value = viewModel.state.shortName, - label = ADDRESS_SHORT_NAME_FIELD, - leadingIcon = Icons.Default.CurrencyRupee, - isError = shortNameError != null, - errorText = shortNameError, - errorTextTag = ADDRESS_SHORT_NAME_ERROR, - onValueChange = { - viewModel.onEvent(AddEditAddressEvent.ShortNameChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardButton( - text = if (addressId == 0) CREATE_NEW_ADDRESS else EDIT_ADDRESS, - enabled = enableBtn, - icon = if (addressId == 0) Icons.Default.Add else Icons.Default.EditLocationAlt, - onClick = { - viewModel.onEvent(AddEditAddressEvent.CreateOrUpdateAddress(addressId)) - } - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/di/CartModule.kt b/app/src/main/java/com/niyaj/poposroom/features/cart/di/CartModule.kt deleted file mode 100644 index d53adcb2..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/di/CartModule.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.niyaj.poposroom.features.cart.di - -import com.niyaj.poposroom.features.cart.data.dao.CartDao -import com.niyaj.poposroom.features.cart.data.repository.CartRepositoryImpl -import com.niyaj.poposroom.features.cart.domain.repository.CartRepository -import com.niyaj.poposroom.features.cart_order.data.dao.CartOrderDao -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.CoroutineDispatcher - -@Module -@InstallIn(SingletonComponent::class) -object CartModule { - - @Provides - fun provideCartDao(database: PoposDatabase) : CartDao { - return database.cartDao() - } - - @Provides - fun provideCartRepository( - cartDao: CartDao, - cartOrderDao: CartOrderDao, - @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, - ): CartRepository { - return CartRepositoryImpl(cartDao, cartOrderDao, ioDispatcher) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/CartScreen.kt b/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/CartScreen.kt deleted file mode 100644 index 90307255..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/CartScreen.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.niyaj.poposroom.features.cart.presentation - -import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Inventory -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavController -import com.niyaj.poposroom.features.cart.presentation.components.CartTabItem -import com.niyaj.poposroom.features.cart.presentation.dine_in.DineInScreen -import com.niyaj.poposroom.features.cart.presentation.dine_out.DineOutScreen -import com.niyaj.poposroom.features.common.components.StandardScaffoldWithBottomNavigation -import com.niyaj.poposroom.features.common.components.Tabs -import com.niyaj.poposroom.features.common.components.TabsContent -import com.niyaj.poposroom.features.destinations.OrderScreenDestination -import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.navigation.navigate - -@OptIn(ExperimentalFoundationApi::class) -@Destination -@Composable -fun CartScreen( - navController: NavController, -) { - val pagerState = rememberPagerState( - initialPage = 0, - initialPageOffsetFraction = 0f, - pageCount = { 2 } - ) - - - StandardScaffoldWithBottomNavigation( - navController = navController, - title = "My Cart", - navActions = { - IconButton( - onClick = { - navController.navigate(OrderScreenDestination()) - }, - ){ - Icon( - imageVector = Icons.Default.Inventory, - contentDescription = "go to order screen", - ) - } - }, - bottomBar = {}, - ) { - val tabs = listOf( - CartTabItem.DineOutItem { - DineOutScreen(navController = navController) - }, - CartTabItem.DineInItem { - DineInScreen(navController = navController) - }, - ) - - Column( - modifier = Modifier - .fillMaxSize(), - ) { - Tabs(tabs = tabs, pagerState = pagerState) - - TabsContent(tabs = tabs, pagerState = pagerState) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInEvent.kt b/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInEvent.kt deleted file mode 100644 index cf6f335b..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInEvent.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.niyaj.poposroom.features.cart.presentation.dine_in - -sealed class DineInEvent { - - data class SelectDineInOrder(val orderId: Int): DineInEvent() - - object SelectAllDineInOrder: DineInEvent() - - data class IncreaseQuantity(val orderId: Int, val productId: Int): DineInEvent() - - data class DecreaseQuantity(val orderId: Int, val productId: Int): DineInEvent() - - data class UpdateAddOnItemInCart(val itemId: Int, val orderId: Int): DineInEvent() - - data class PlaceDineInOrder(val orderId: Int): DineInEvent() - - object PlaceAllDineInOrder: DineInEvent() - - object RefreshDineInOrder: DineInEvent() -} diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInState.kt b/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInState.kt deleted file mode 100644 index f662f537..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.niyaj.poposroom.features.cart.presentation.dine_in - -import com.niyaj.poposroom.features.cart.domain.model.CartItem - -data class DineInState( - val isLoading: Boolean = true, - val items: List = emptyList() -) diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutState.kt b/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutState.kt deleted file mode 100644 index ffceba3c..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.niyaj.poposroom.features.cart.presentation.dine_out - -import com.niyaj.poposroom.features.cart.domain.model.CartItem - -data class DineOutState( - val isLoading: Boolean = true, - val items: List = emptyList(), -) diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/model/CartOrderEntity.kt b/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/model/CartOrderEntity.kt deleted file mode 100644 index 4ea61b62..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/model/CartOrderEntity.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.niyaj.poposroom.features.cart_order.domain.model - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderStatus -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.utils.toTime -import com.niyaj.poposroom.features.customer.domain.model.Customer -import java.util.Date - - -@Entity(tableName = "cartorder") -data class CartOrderEntity( - @PrimaryKey(autoGenerate = true) - val orderId: Int, - - val orderType: OrderType = OrderType.DineIn, - - val orderStatus: OrderStatus = OrderStatus.PROCESSING, - - val doesChargesIncluded: Boolean = false, - - @ColumnInfo(index = true) - val addressId: Int, - - @ColumnInfo(index = true) - val customerId: Int, - - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") - val createdAt: Date = Date(), - - @ColumnInfo(defaultValue = "NULL") - val updatedAt: Date? = null, -) - - -data class CartOrder( - val orderId: Int = 0, - - val orderType: OrderType = OrderType.DineIn, - - val orderStatus: OrderStatus = OrderStatus.PROCESSING, - - val doesChargesIncluded: Boolean = false, - - val customer: Customer = Customer(), - - val address: Address = Address(), - - val createdAt: Date = Date(), - - val updatedAt: Date? = null, -) - - -fun List.filterCartOrder(searchText: String): List { - return if (searchText.isNotEmpty()){ - this.filter {cartOrder -> - cartOrder.orderStatus.name.contains(searchText, true) || - cartOrder.customer.customerPhone.contains(searchText, true) || - cartOrder.customer.customerName?.contains(searchText, true) == true || - cartOrder.address.addressName.contains(searchText, true) || - cartOrder.address.shortName.contains(searchText, true) || - cartOrder.orderType.name.contains(searchText, true) || - cartOrder.orderId.toString().contains(searchText, true) || - cartOrder.createdAt.toTime.contains(searchText, true) || - cartOrder.updatedAt?.toTime?.contains(searchText, true) == true - } - }else this -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/utils/OrderStatus.kt b/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/utils/OrderStatus.kt deleted file mode 100644 index 137f2767..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/utils/OrderStatus.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.niyaj.poposroom.features.cart_order.domain.utils - -enum class OrderStatus { - PROCESSING, - PLACED -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/utils/OrderType.kt b/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/utils/OrderType.kt deleted file mode 100644 index a97715d0..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/utils/OrderType.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.niyaj.poposroom.features.cart_order.domain.utils - -enum class OrderType { - DineIn, - DineOut, -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderState.kt b/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderState.kt deleted file mode 100644 index 1b886df5..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.niyaj.poposroom.features.cart_order.presentation.add_edit - -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType - -data class AddEditCartOrderState( - val orderType: OrderType = OrderType.DineIn, - val doesChargesIncluded: Boolean = false, -) diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryScreen.kt b/app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryScreen.kt deleted file mode 100644 index 89f36dd6..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryScreen.kt +++ /dev/null @@ -1,121 +0,0 @@ -package com.niyaj.poposroom.features.category.presentation.add_edit - -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.width -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Category -import androidx.compose.material.icons.filled.Edit -import androidx.compose.material3.Checkbox -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.ADD_EDIT_CATEGORY_SCREEN -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.CATEGORY_AVAILABLE_SWITCH -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.CATEGORY_NAME_ERROR_TAG -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.CATEGORY_NAME_FIELD -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.CREATE_NEW_CATEGORY -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.UPDATE_CATEGORY -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardTextField -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent - -@Composable -fun AddEditCategoryScreen( - categoryId: Int = 0, - closeSheet: () -> Unit, - viewModel: AddEditCategoryViewModel = hiltViewModel(), -) { - val nameError = viewModel.nameError.collectAsStateWithLifecycle().value - - val enableBtn = nameError == null - - val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value - - LaunchedEffect(key1 = Unit) { - viewModel.resetFields() - } - - LaunchedEffect(key1 = event) { - event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} - is UiEvent.OnError -> { - closeSheet() - } - is UiEvent.OnSuccess -> { - closeSheet() - } - } - } - } - - LaunchedEffect(key1 = categoryId) { - viewModel.getCategoryById(categoryId) - } - - Column( - modifier = Modifier - .testTag(ADD_EDIT_CATEGORY_SCREEN) - .fillMaxWidth(), - verticalArrangement = Arrangement.Center, - ) { - StandardTextField( - value = viewModel.addEditState.categoryName, - label = CATEGORY_NAME_FIELD, - leadingIcon = Icons.Default.Category, - isError = nameError != null, - errorText = nameError, - errorTextTag = CATEGORY_NAME_ERROR_TAG, - onValueChange = { - viewModel.onEvent(AddEditCategoryEvent.CategoryNameChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Checkbox( - modifier = Modifier.testTag(CATEGORY_AVAILABLE_SWITCH), - checked = viewModel.addEditState.isAvailable, - onCheckedChange = { - viewModel.onEvent(AddEditCategoryEvent.CategoryAvailabilityChanged) - } - ) - Spacer(modifier = Modifier.width(SpaceSmall)) - Text( - text = if(viewModel.addEditState.isAvailable) - "Marked as available" - else - "Marked as not available", - style = MaterialTheme.typography.labelMedium - ) - } - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardButton( - text = if (categoryId == 0) CREATE_NEW_CATEGORY else UPDATE_CATEGORY, - icon = if (categoryId == 0) Icons.Default.Add else Icons.Default.Edit, - enabled = enableBtn, - onClick = { - viewModel.onEvent(AddEditCategoryEvent.CreateUpdateAddEditCategory(categoryId)) - } - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesScreen.kt b/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesScreen.kt deleted file mode 100644 index f33d55b3..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesScreen.kt +++ /dev/null @@ -1,144 +0,0 @@ -package com.niyaj.poposroom.features.charges.presentation.add_edit - -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.width -import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Category -import androidx.compose.material.icons.filled.CurrencyRupee -import androidx.compose.material.icons.filled.Edit -import androidx.compose.material3.Checkbox -import androidx.compose.material3.MaterialTheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.text.input.KeyboardType -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.ADD_EDIT_CHARGES_BUTTON -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.ADD_EDIT_CHARGES_SCREEN -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CHARGES_AMOUNT_ERROR -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CHARGES_AMOUNT_FIELD -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CHARGES_APPLIED_SWITCH -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CHARGES_NAME_ERROR -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CHARGES_NAME_FIELD -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CREATE_NEW_CHARGES -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.EDIT_CHARGES_ITEM -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardTextField -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.safeString - -@Composable -fun AddEditChargesScreen( - chargesId: Int = 0, - closeSheet: () -> Unit, - viewModel: AddEditChargesViewModel = hiltViewModel(), -) { - val nameError = viewModel.nameError.collectAsStateWithLifecycle().value - val priceError = viewModel.priceError.collectAsStateWithLifecycle().value - - val enableBtn = nameError == null && priceError == null - - val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value - - LaunchedEffect(key1 = Unit) { - viewModel.resetFields() - } - - LaunchedEffect(key1 = event) { - event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} - is UiEvent.OnError -> { - closeSheet() - } - is UiEvent.OnSuccess -> { - closeSheet() - } - } - } - } - - LaunchedEffect(key1 = chargesId) { - viewModel.getChargesById(chargesId) - } - - Column( - modifier = Modifier - .testTag(ADD_EDIT_CHARGES_SCREEN) - .fillMaxWidth(), - verticalArrangement = Arrangement.Center, - ) { - StandardTextField( - value = viewModel.addEditState.chargesName, - label = CHARGES_NAME_FIELD, - leadingIcon = Icons.Default.Category, - isError = nameError != null, - errorText = nameError, - errorTextTag = CHARGES_NAME_ERROR, - onValueChange = { - viewModel.onEvent(AddEditChargesEvent.ChargesNameChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardTextField( - value = viewModel.addEditState.chargesPrice.safeString, - label = CHARGES_AMOUNT_FIELD, - leadingIcon = Icons.Default.CurrencyRupee, - isError = priceError != null, - errorText = priceError, - keyboardType = KeyboardType.Number, - errorTextTag = CHARGES_AMOUNT_ERROR, - onValueChange = { - viewModel.onEvent(AddEditChargesEvent.ChargesPriceChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Checkbox( - modifier = Modifier.testTag(CHARGES_APPLIED_SWITCH), - checked = viewModel.addEditState.chargesApplicable, - onCheckedChange = { - viewModel.onEvent(AddEditChargesEvent.ChargesApplicableChanged) - } - ) - Spacer(modifier = Modifier.width(SpaceSmall)) - Text( - text = if(viewModel.addEditState.chargesApplicable) - "Marked as applied" - else - "Marked as not applied", - style = MaterialTheme.typography.labelMedium - ) - } - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardButton( - modifier = Modifier.testTag(ADD_EDIT_CHARGES_BUTTON), - text = if (chargesId == 0) CREATE_NEW_CHARGES else EDIT_CHARGES_ITEM, - icon = if (chargesId == 0) Icons.Default.Add else Icons.Default.Edit, - enabled = enableBtn, - onClick = { - viewModel.onEvent(AddEditChargesEvent.CreateOrUpdateCharges(chargesId)) - } - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardScaffold.kt b/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardScaffold.kt deleted file mode 100644 index de97b3f8..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardScaffold.kt +++ /dev/null @@ -1,1299 +0,0 @@ -package com.niyaj.poposroom.features.common.components - -import android.view.animation.OvershootInterpolator -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.FastOutLinearInEasing -import androidx.compose.animation.core.LinearEasing -import androidx.compose.animation.core.LinearOutSlowInEasing -import androidx.compose.animation.core.MutableTransitionState -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut -import androidx.compose.animation.slideInVertically -import androidx.compose.animation.slideOutVertically -import androidx.compose.animation.togetherWith -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.WindowInsets -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.windowInsetsPadding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.IconButton -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Apps -import androidx.compose.material.icons.filled.ArrowBack -import androidx.compose.material.icons.filled.Checklist -import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.Delete -import androidx.compose.material.icons.filled.Edit -import androidx.compose.material.icons.filled.Search -import androidx.compose.material.icons.filled.Settings -import androidx.compose.material.icons.outlined.Assessment -import androidx.compose.material.icons.outlined.Home -import androidx.compose.material.icons.outlined.Inventory2 -import androidx.compose.material.icons.outlined.ShoppingCart -import androidx.compose.material.icons.rounded.Assessment -import androidx.compose.material.icons.rounded.Home -import androidx.compose.material.icons.rounded.Inventory2 -import androidx.compose.material.icons.rounded.ShoppingCart -import androidx.compose.material3.BottomAppBar -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.CenterAlignedTopAppBar -import androidx.compose.material3.DrawerValue -import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.ExtendedFloatingActionButton -import androidx.compose.material3.FabPosition -import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.Icon -import androidx.compose.material3.LargeTopAppBar -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ModalNavigationDrawer -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarDefaults -import androidx.compose.material3.NavigationBarItem -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SnackbarHost -import androidx.compose.material3.SnackbarHostState -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBarDefaults -import androidx.compose.material3.rememberDrawerState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.Stable -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.rememberUpdatedState -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.lerp -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.lerp -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import com.exyte.animatednavbar.AnimatedNavigationBar -import com.exyte.animatednavbar.animation.balltrajectory.Teleport -import com.exyte.animatednavbar.animation.indendshape.Height -import com.exyte.animatednavbar.animation.indendshape.shapeCornerRadius -import com.exyte.animatednavbar.items.dropletbutton.DropletButton -import com.google.accompanist.systemuicontroller.rememberSystemUiController -import com.niyaj.poposroom.R -import com.niyaj.poposroom.features.common.ui.theme.LightColor8 -import com.niyaj.poposroom.features.common.ui.theme.Purple -import com.niyaj.poposroom.features.common.ui.theme.SpaceMedium -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.Constants -import com.niyaj.poposroom.features.common.utils.Constants.DELETE_ICON -import com.niyaj.poposroom.features.common.utils.Constants.EDIT_ICON -import com.niyaj.poposroom.features.common.utils.Constants.FAB_TEXT -import com.niyaj.poposroom.features.common.utils.Constants.SEARCH_ICON -import com.niyaj.poposroom.features.common.utils.Constants.SELECTALL_ICON -import com.niyaj.poposroom.features.common.utils.Constants.SETTINGS_ICON -import com.niyaj.poposroom.features.common.utils.Constants.STANDARD_BACK_BUTTON -import com.niyaj.poposroom.features.destinations.AddEditCartOrderScreenDestination -import com.niyaj.poposroom.features.destinations.CartOrderScreenDestination -import com.niyaj.poposroom.features.destinations.CartScreenDestination -import com.niyaj.poposroom.features.destinations.MainFeedScreenDestination -import com.niyaj.poposroom.features.destinations.OrderScreenDestination -import com.niyaj.poposroom.features.destinations.SelectOrderScreenDestination -import com.niyaj.poposroom.features.main_feed.domain.utils.MainFeedTestTags -import com.ramcosta.composedestinations.navigation.navigate -import kotlinx.coroutines.launch - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun StandardScaffold( - modifier: Modifier = Modifier, - navController: NavController, - title: String, - floatingActionButton: @Composable () -> Unit = {}, - fabPosition: FabPosition = FabPosition.Center, - searchText: String = "", - placeholderText: String = "Search for items", - showSettings: Boolean = true, - selectionCount: Int = 0, - showBottomBarActions: Boolean = false, - showSearchBar: Boolean = false, - showBackButton: Boolean = false, - onClearClick: () -> Unit = {}, - onDeselect: () -> Unit = {}, - onSearchClick: () -> Unit = {}, - onSettingsClick: () -> Unit = {}, - onEditClick: () -> Unit = {}, - onDeleteClick: () -> Unit = {}, - onSelectAllClick: () -> Unit = {}, - onSearchTextChanged: (String) -> Unit = {}, - onBackClick: () -> Unit = {}, - snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, - content: @Composable (PaddingValues) -> Unit, -) { - val drawerState = rememberDrawerState(DrawerValue.Closed) - val scope = rememberCoroutineScope() - val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() - - // Remember a SystemUiController - val systemUiController = rememberSystemUiController() - - val colorTransitionFraction = scrollBehavior.state.collapsedFraction - - val color = rememberUpdatedState(newValue = containerColor(colorTransitionFraction)) - val shape = rememberUpdatedState(newValue = containerShape(colorTransitionFraction)) - - val selectedState = MutableTransitionState(selectionCount) - - SideEffect { - systemUiController.setStatusBarColor( - color = color.value, - darkIcons = true, - ) - - systemUiController.setNavigationBarColor( - color = color.value - ) - } - - ModalNavigationDrawer( - drawerState = drawerState, - drawerContent = { - StandardDrawer( - navController = navController, - onCloseClick = { - scope.launch { - drawerState.close() - } - } - ) - }, - gesturesEnabled = true - ) { - Scaffold( - topBar = { - LargeTopAppBar( - title = { - Text(text = title) - }, - navigationIcon = { - if (showBackButton) { - IconButton( - onClick = onBackClick, - modifier = Modifier.testTag(STANDARD_BACK_BUTTON) - ) { - Icon( - imageVector = Icons.Default.ArrowBack, - contentDescription = null, - tint = MaterialTheme.colorScheme.scrim - ) - } - } else { - AnimatedContent( - targetState = selectedState, - transitionSpec = { - (fadeIn()).togetherWith( - fadeOut(animationSpec = tween(200)) - ) - }, - label = "navigationIcon", - contentKey = { - it - } - ) { state -> - if (state.currentState != 0) { - IconButton( - onClick = onDeselect - ) { - Icon( - imageVector = Icons.Default.Close, - contentDescription = Constants.CLEAR_ICON - ) - } - } else { - IconButton( - onClick = { - scope.launch { - drawerState.open() - } - } - ) { - Icon( - imageVector = Icons.Default.Apps, - contentDescription = null - ) - } - } - } - } - }, - actions = { - if (showSearchBar) { - StandardSearchBar( - searchText = searchText, - placeholderText = placeholderText, - onClearClick = onClearClick, - onSearchTextChanged = onSearchTextChanged - ) - } else { - AnimatedContent( - targetState = selectedState, - transitionSpec = { - (fadeIn()).togetherWith( - fadeOut(animationSpec = tween(200)) - ) - }, - label = "navActions", - ) { state -> - Row { - if (state.currentState != 0) { - if (!showBottomBarActions) { - if (state.currentState == 1) { - IconButton(onClick = onEditClick) { - Icon( - imageVector = Icons.Default.Edit, - contentDescription = EDIT_ICON - ) - } - } - - IconButton(onClick = onDeleteClick) { - Icon( - imageVector = Icons.Default.Delete, - contentDescription = DELETE_ICON - ) - } - - IconButton(onClick = onSelectAllClick) { - Icon( - imageVector = Icons.Default.Checklist, - contentDescription = SELECTALL_ICON - ) - } - } - } else { - IconButton(onClick = onSearchClick) { - Icon( - imageVector = Icons.Default.Search, - contentDescription = SEARCH_ICON - ) - } - - if (showSettings) { - IconButton(onClick = onSettingsClick) { - Icon( - imageVector = Icons.Default.Settings, - contentDescription = SETTINGS_ICON - ) - } - } - } - } - } - } - }, - scrollBehavior = scrollBehavior, - ) - }, - bottomBar = { - if (showBottomBarActions) { - AnimatedVisibility( - visible = selectionCount != 0, - label = "BottomBar", - enter = fadeIn() + slideInVertically( - initialOffsetY = { fullHeight -> - fullHeight / 4 - } - ), - exit = fadeOut() + slideOutVertically( - targetOffsetY = { fullHeight -> - fullHeight / 4 - } - ) - ) { - BottomAppBar( - actions = { - AnimatedContent( - targetState = selectedState, - transitionSpec = { - fadeIn().togetherWith(fadeOut(tween(200))) - }, - label = "bottomActions", - contentKey = { - it.currentState - } - ) { state -> - Row { - IconButton(onClick = onSelectAllClick) { - Icon( - imageVector = Icons.Default.Checklist, - contentDescription = SELECTALL_ICON - ) - } - - if (state.currentState == 1) { - IconButton(onClick = onEditClick) { - Icon( - imageVector = Icons.Default.Edit, - contentDescription = EDIT_ICON - ) - } - } - - IconButton(onClick = onDeleteClick) { - Icon( - imageVector = Icons.Default.Delete, - contentDescription = DELETE_ICON - ) - } - } - } - } - ) - } - } - }, - floatingActionButton = floatingActionButton, - floatingActionButtonPosition = fabPosition, - snackbarHost = { SnackbarHost(snackbarHostState) }, - modifier = modifier - .testTag(title) - .fillMaxSize(), - ) { padding -> - ElevatedCard( - modifier = Modifier - .fillMaxSize() - .padding(padding) - .nestedScroll(scrollBehavior.nestedScrollConnection), - shape = shape.value, - colors = CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.onPrimary - ) - ) { - content(padding) - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun StandardScaffold( - modifier: Modifier = Modifier, - navController: NavController, - title: String, - floatingActionButton: @Composable () -> Unit, - navActions: @Composable () -> Unit, - fabPosition: FabPosition = FabPosition.Center, - selectionCount: Int, - showBottomBarActions: Boolean = false, - showBackButton: Boolean = false, - onDeselect: () -> Unit = {}, - onEditClick: () -> Unit = {}, - onDeleteClick: () -> Unit = {}, - onSelectAllClick: () -> Unit = {}, - onBackClick: () -> Unit = {}, - snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, - content: @Composable (PaddingValues) -> Unit, -) { - val drawerState = rememberDrawerState(DrawerValue.Closed) - val scope = rememberCoroutineScope() - val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() - - // Remember a SystemUiController - val systemUiController = rememberSystemUiController() - - val colorTransitionFraction = scrollBehavior.state.collapsedFraction - - val color = rememberUpdatedState(newValue = containerColor(colorTransitionFraction)) - val shape = rememberUpdatedState(newValue = containerShape(colorTransitionFraction)) - - val selectedState = MutableTransitionState(selectionCount) - - SideEffect { - systemUiController.setStatusBarColor( - color = color.value, - darkIcons = true, - ) - - systemUiController.setNavigationBarColor( - color = color.value - ) - } - - ModalNavigationDrawer( - drawerState = drawerState, - drawerContent = { - StandardDrawer( - navController = navController, - onCloseClick = { - scope.launch { - drawerState.close() - } - } - ) - }, - gesturesEnabled = true - ) { - Scaffold( - topBar = { - LargeTopAppBar( - title = { - Text(text = title) - }, - navigationIcon = { - if (showBackButton) { - IconButton( - onClick = onBackClick, - modifier = Modifier.testTag(STANDARD_BACK_BUTTON) - ) { - Icon( - imageVector = Icons.Default.ArrowBack, - contentDescription = null, - tint = MaterialTheme.colorScheme.scrim - ) - } - } else { - AnimatedContent( - targetState = selectedState, - transitionSpec = { - (fadeIn()).togetherWith( - fadeOut(animationSpec = tween(200)) - ) - }, - label = "navigationIcon", - contentKey = { - it - } - ) { state -> - if (state.currentState != 0) { - IconButton( - onClick = onDeselect - ) { - Icon( - imageVector = Icons.Default.Close, - contentDescription = Constants.CLEAR_ICON - ) - } - } else { - IconButton( - onClick = { - scope.launch { - drawerState.open() - } - } - ) { - Icon( - imageVector = Icons.Default.Apps, - contentDescription = null - ) - } - } - } - } - }, - actions = { - navActions() - }, - scrollBehavior = scrollBehavior, - ) - }, - bottomBar = { - if (showBottomBarActions) { - AnimatedVisibility( - visible = selectionCount != 0, - label = "BottomBar", - enter = fadeIn() + slideInVertically( - initialOffsetY = { fullHeight -> - fullHeight / 4 - } - ), - exit = fadeOut() + slideOutVertically( - targetOffsetY = { fullHeight -> - fullHeight / 4 - } - ) - ) { - BottomAppBar( - actions = { - AnimatedContent( - targetState = selectedState, - transitionSpec = { - fadeIn().togetherWith(fadeOut(tween(200))) - }, - label = "bottomActions", - contentKey = { - it.currentState - } - ) { state -> - Row { - IconButton(onClick = onSelectAllClick) { - Icon( - imageVector = Icons.Default.Checklist, - contentDescription = SELECTALL_ICON - ) - } - - if (state.currentState == 1) { - IconButton(onClick = onEditClick) { - Icon( - imageVector = Icons.Default.Edit, - contentDescription = EDIT_ICON - ) - } - } - - IconButton(onClick = onDeleteClick) { - Icon( - imageVector = Icons.Default.Delete, - contentDescription = DELETE_ICON - ) - } - } - } - } - ) - } - } - }, - floatingActionButton = floatingActionButton, - floatingActionButtonPosition = fabPosition, - snackbarHost = { SnackbarHost(snackbarHostState) }, - modifier = modifier - .testTag(title) - .fillMaxSize(), - ) { padding -> - ElevatedCard( - modifier = Modifier - .fillMaxSize() - .padding(padding) - .nestedScroll(scrollBehavior.nestedScrollConnection), - shape = shape.value, - elevation = CardDefaults.cardElevation(), - colors = CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.onPrimary - ) - ) { - content(padding) - } - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun StandardScaffoldNew( - navController: NavController, - modifier: Modifier = Modifier, - title: String, - showDrawer: Boolean = true, - showBackButton: Boolean = false, - showBottomBar: Boolean, - fabPosition: FabPosition = FabPosition.Center, - snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, - onBackClick: () -> Unit = { navController.navigateUp() }, - navigationIcon: @Composable () -> Unit = {}, - navActions: @Composable RowScope.() -> Unit = {}, - floatingActionButton: @Composable () -> Unit = {}, - bottomBar: @Composable () -> Unit = { AnimatedBottomNavigationBar(navController) }, - content: @Composable (PaddingValues) -> Unit = {}, -) { - val drawerState = rememberDrawerState(DrawerValue.Closed) - val scope = rememberCoroutineScope() - val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() - - // Remember a SystemUiController - val systemUiController = rememberSystemUiController() - - val colorTransitionFraction = scrollBehavior.state.collapsedFraction - - val color = rememberUpdatedState(newValue = containerColor(colorTransitionFraction)) - val shape = rememberUpdatedState(newValue = containerShape(colorTransitionFraction)) - val navColor = MaterialTheme.colorScheme.surface - - SideEffect { - systemUiController.setStatusBarColor( - color = color.value, - darkIcons = true, - ) - - systemUiController.setNavigationBarColor( - color = navColor - ) - } - - ModalNavigationDrawer( - drawerState = drawerState, - drawerContent = { - StandardDrawer( - navController = navController, - onCloseClick = { - scope.launch { - drawerState.close() - } - } - ) - }, - gesturesEnabled = true - ) { - Scaffold( - topBar = { - LargeTopAppBar( - title = { - Text(text = title) - }, - navigationIcon = { - if (showBackButton) { - IconButton( - onClick = onBackClick, - modifier = Modifier.testTag(STANDARD_BACK_BUTTON) - ) { - Icon( - imageVector = Icons.Default.ArrowBack, - contentDescription = null, - tint = MaterialTheme.colorScheme.scrim - ) - } - } else if (showDrawer) { - IconButton( - onClick = { - scope.launch { - drawerState.open() - } - } - ) { - Icon( - imageVector = Icons.Default.Apps, - contentDescription = null - ) - } - } else navigationIcon() - }, - actions = { - navActions() - }, - scrollBehavior = scrollBehavior, - ) - }, - bottomBar = { - AnimatedVisibility( - visible = showBottomBar, - label = "BottomBar", - enter = fadeIn() + slideInVertically( - initialOffsetY = { fullHeight -> - fullHeight / 4 - } - ), - exit = fadeOut() + slideOutVertically( - targetOffsetY = { fullHeight -> - fullHeight / 4 - } - ) - ) { - bottomBar() - } - }, - floatingActionButton = floatingActionButton, - floatingActionButtonPosition = fabPosition, - snackbarHost = { SnackbarHost(snackbarHostState) }, - modifier = modifier - .testTag(title) - .fillMaxSize() - .nestedScroll(scrollBehavior.nestedScrollConnection), - ) { padding -> - ElevatedCard( - modifier = Modifier - .fillMaxSize() - .padding(padding), - shape = shape.value, - elevation = CardDefaults.cardElevation(), - colors = CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.onPrimary - ) - ) { - content(padding) - } - } - } -} - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun StandardScaffoldWithBottomNavigation( - modifier: Modifier = Modifier, - navController: NavController, - title: String = "", - selectedId: String = "0", - showBottomBar: Boolean = false, - showFab: Boolean = false, - showSearchBar: Boolean = false, - showSearchIcon: Boolean = false, - searchText: String = "", - openSearchBar: () -> Unit = {}, - closeSearchBar: () -> Unit = {}, - onSearchTextChanged: (String) -> Unit = {}, - onClearClick: () -> Unit = {}, - snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, - bottomBar: @Composable () -> Unit = { AnimatedBottomNavigationBar(navController = navController) }, - navActions: @Composable RowScope.() -> Unit = {}, - content: @Composable (PaddingValues) -> Unit, -) { - val drawerState = rememberDrawerState(DrawerValue.Closed) - val scope = rememberCoroutineScope() - val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() - - // Remember a SystemUiController - val systemUiController = rememberSystemUiController() - - val colorTransitionFraction = scrollBehavior.state.collapsedFraction - - val shape = rememberUpdatedState(newValue = containerShape(colorTransitionFraction)) - val statusColor = MaterialTheme.colorScheme.surface - val navColor = MaterialTheme.colorScheme.surfaceVariant - - SideEffect { - systemUiController.setStatusBarColor(color = statusColor, darkIcons = true) - - systemUiController.setNavigationBarColor(color = navColor) - } - - ModalNavigationDrawer( - drawerState = drawerState, - drawerContent = { - StandardDrawer( - navController = navController, - onCloseClick = { - scope.launch { - drawerState.close() - } - } - ) - }, - gesturesEnabled = true - ) { - Scaffold( - topBar = { - CenterAlignedTopAppBar( - title = { - if (title.isNotEmpty()) { - Text(text = title) - } else if (selectedId != "0") { - SelectedOrderBox( - modifier = Modifier - .padding(horizontal = SpaceMedium), - text = selectedId, - height = 40.dp, - onClick = { - navController.navigate(SelectOrderScreenDestination()) - } - ) - } - }, - navigationIcon = { - if (showSearchBar) { - IconButton( - onClick = closeSearchBar, - modifier = Modifier.testTag(STANDARD_BACK_BUTTON) - ) { - Icon( - imageVector = Icons.Default.ArrowBack, - contentDescription = null, - tint = MaterialTheme.colorScheme.scrim - ) - } - } else { - IconButton( - onClick = { - scope.launch { - drawerState.open() - } - } - ) { - Icon( - imageVector = Icons.Default.Apps, - contentDescription = null - ) - } - } - }, - actions = { - if (showSearchBar) { - StandardSearchBar( - searchText = searchText, - placeholderText = MainFeedTestTags.MAIN_SEARCH_PLACEHOLDER, - onClearClick = onClearClick, - onSearchTextChanged = onSearchTextChanged - ) - } else if (showSearchIcon) { - IconButton( - onClick = openSearchBar - ) { - Icon( - imageVector = Icons.Default.Search, - contentDescription = SEARCH_ICON - ) - } - } else { - navActions() - } - }, - scrollBehavior = scrollBehavior, - colors = TopAppBarDefaults.centerAlignedTopAppBarColors( - containerColor = MaterialTheme.colorScheme.surface, - scrolledContainerColor = MaterialTheme.colorScheme.surface - ) - ) - }, - floatingActionButton = { - AnimatedVisibility( - visible = showFab - ) { - FloatingActionButton( - onClick = { - navController.navigate(AddEditCartOrderScreenDestination()) - }, - containerColor = MaterialTheme.colorScheme.secondary - ) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = "Create new order" - ) - } - } - }, - floatingActionButtonPosition = FabPosition.End, - bottomBar = { - AnimatedVisibility( - visible = showBottomBar, - label = "BottomBar", - enter = fadeIn() + slideInVertically( - initialOffsetY = { fullHeight -> - fullHeight / 4 - } - ), - exit = fadeOut() + slideOutVertically( - targetOffsetY = { fullHeight -> - fullHeight / 4 - } - ) - ) { - bottomBar() - } - }, - snackbarHost = { SnackbarHost(snackbarHostState) }, - modifier = modifier - .testTag(title) - .fillMaxSize() - .nestedScroll(scrollBehavior.nestedScrollConnection), - ) { padding -> - ElevatedCard( - modifier = Modifier - .fillMaxSize() - .padding(padding), - shape = shape.value, - elevation = CardDefaults.cardElevation(), - colors = CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.onPrimary - ) - ) { - content(padding) - } - } - } -} - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun StandardScaffoldWithOutDrawer( - title: String, - onBackClick: () -> Unit, - showBottomBar: Boolean = false, - bottomBar: @Composable () -> Unit = {}, - content: @Composable () -> Unit -) { - val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() - // Remember a SystemUiController - val systemUiController = rememberSystemUiController() - - val colorTransitionFraction = scrollBehavior.state.collapsedFraction - - val color = rememberUpdatedState(newValue = containerColor(colorTransitionFraction)) - val shape = rememberUpdatedState(newValue = containerShape(colorTransitionFraction)) - - SideEffect { - systemUiController.setStatusBarColor( - color = color.value, - darkIcons = true, - ) - - systemUiController.setNavigationBarColor( - color = color.value - ) - } - - Scaffold( - modifier = Modifier - .testTag(title) - .fillMaxWidth() - .nestedScroll(scrollBehavior.nestedScrollConnection), - topBar = { - LargeTopAppBar( - navigationIcon = { - IconButton( - onClick = onBackClick, - modifier = Modifier.testTag(STANDARD_BACK_BUTTON) - ) { - Icon( - imageVector = Icons.Default.ArrowBack, - contentDescription = null, - tint = MaterialTheme.colorScheme.scrim - ) - } - }, - title = { - Text(text = title) - }, - scrollBehavior = scrollBehavior, - ) - }, - bottomBar = { - AnimatedVisibility( - visible = showBottomBar, - enter = fadeIn() + slideInVertically( - initialOffsetY = { fullHeight -> - fullHeight / 4 - } - ), - exit = fadeOut() + slideOutVertically( - targetOffsetY = { fullHeight -> - fullHeight / 4 - } - ) - ) { - BottomAppBar { - bottomBar() - } - } - } - ) { padding -> - ElevatedCard( - modifier = Modifier - .fillMaxSize() - .padding(padding), - shape = shape.value, - colors = CardDefaults.elevatedCardColors( - containerColor = MaterialTheme.colorScheme.surface - ), - elevation = CardDefaults.elevatedCardElevation( - defaultElevation = colorTransitionFraction.dp - ) - ) { - content() - } - } -} - - -@Composable -fun StandardFAB( - fabVisible: Boolean, - showScrollToTop: Boolean = false, - fabText: String = FAB_TEXT, - fabIcon: ImageVector = Icons.Filled.Add, - containerColor: Color = MaterialTheme.colorScheme.tertiaryContainer, - onFabClick: () -> Unit, - onClickScroll: () -> Unit, -) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - ) { - AnimatedVisibility( - visible = showScrollToTop, - enter = fadeIn(), - exit = fadeOut(), - ) { - ScrollToTop(onClick = onClickScroll, containerColor = containerColor) - } - - Spacer(modifier = Modifier.height(SpaceSmall)) - - AnimatedVisibility( - visible = fabVisible, - enter = fadeIn() + slideInVertically( - initialOffsetY = { fullHeight -> - fullHeight / 4 - } - ), - exit = fadeOut() + slideOutVertically( - targetOffsetY = { fullHeight -> - fullHeight / 4 - } - ), - label = "FloatingActionButton" - ) { - ExtendedFloatingActionButton( - containerColor = MaterialTheme.colorScheme.primary, - onClick = onFabClick, - expanded = !showScrollToTop, - icon = { Icon(fabIcon, fabText) }, - text = { Text(text = fabText.uppercase()) }, - ) - } - } -} - - -@Composable -fun BottomNavigationBar( - navController: NavController -) { - val currentRoute = navController.currentBackStackEntry?.destination?.route - - NavigationBar { - NavigationBarItem( - selected = currentRoute == MainFeedScreenDestination.route, - label = { - val fontWeight = if (currentRoute == MainFeedScreenDestination.route) - FontWeight.SemiBold else FontWeight.Normal - - Text( - text = "Home", - style = MaterialTheme.typography.labelSmall, - fontWeight = fontWeight, - ) - }, - onClick = { - navController.navigate(MainFeedScreenDestination()) - }, - icon = { - val icon = if (currentRoute == MainFeedScreenDestination.route) { - Icons.Rounded.Home - } else Icons.Outlined.Home - - Icon(imageVector = icon, contentDescription = "Home") - } - ) - - NavigationBarItem( - selected = currentRoute == CartScreenDestination.route, - label = { - val fontWeight = if (currentRoute == CartScreenDestination.route) - FontWeight.SemiBold else FontWeight.Normal - - Text( - text = "Cart", - style = MaterialTheme.typography.labelSmall, - fontWeight = fontWeight, - ) - }, - onClick = { - navController.navigate(CartScreenDestination()) - }, - icon = { - val icon = if (currentRoute == CartScreenDestination.route) { - Icons.Rounded.ShoppingCart - } else Icons.Outlined.ShoppingCart - - Icon(imageVector = icon, contentDescription = "Cart") - } - ) - - NavigationBarItem( - selected = currentRoute == OrderScreenDestination.route, - label = { - val fontWeight = if (currentRoute == OrderScreenDestination.route) - FontWeight.SemiBold else FontWeight.Normal - - Text( - text = "Orders", - style = MaterialTheme.typography.labelSmall, - fontWeight = fontWeight, - ) - }, - onClick = { - navController.navigate(OrderScreenDestination()) - }, - icon = { - val icon = if (currentRoute == OrderScreenDestination.route) { - Icons.Rounded.Inventory2 - } else Icons.Outlined.Inventory2 - - Icon(imageVector = icon, contentDescription = "Orders") - } - ) - - NavigationBarItem( - selected = currentRoute == CartOrderScreenDestination.route, - label = { - val fontWeight = if (currentRoute == CartOrderScreenDestination.route) - FontWeight.SemiBold else FontWeight.Normal - - Text( - text = "Reports", - style = MaterialTheme.typography.labelSmall, - fontWeight = fontWeight, - ) - }, - onClick = { - navController.navigate(CartOrderScreenDestination()) - }, - icon = { - val icon = if (currentRoute == CartOrderScreenDestination.route) { - Icons.Rounded.Assessment - } else Icons.Outlined.Assessment - - Icon(imageVector = icon, contentDescription = "Reports") - } - ) - } -} - -@Composable -fun AnimatedBottomNavigationBar( - navController: NavController, - tonalElevation: Dp = NavigationBarDefaults.Elevation, - windowInsets: WindowInsets = NavigationBarDefaults.windowInsets, -) { - val currentRoute = navController.currentBackStackEntry?.destination?.route.hashCode() - - val navItems = listOf( - NavigationItem( - index = MainFeedScreenDestination.route.hashCode(), - name = "Home", - selected = currentRoute == MainFeedScreenDestination.route.hashCode(), - selectedIcon = R.drawable.round_home, - unselectedIcon = R.drawable.outline_home, - onClick = { - navController.navigate(MainFeedScreenDestination()) - } - ), - NavigationItem( - index = CartScreenDestination.route.hashCode(), - name = "Cart", - selected = currentRoute == CartScreenDestination.route.hashCode(), - selectedIcon = R.drawable.round_cart, - unselectedIcon = R.drawable.outline_cart, - onClick = { - navController.navigate(CartScreenDestination()) - } - ), - NavigationItem( - index = OrderScreenDestination.route.hashCode(), - name = "Orders", - selected = currentRoute == OrderScreenDestination.route.hashCode(), - selectedIcon = R.drawable.round_orders, - unselectedIcon = R.drawable.outline_orders, - onClick = { - navController.navigate(OrderScreenDestination()) - } - ), - NavigationItem( - index = OrderScreenDestination.route.hashCode(), - name = "Reports", - selected = currentRoute == OrderScreenDestination.route.hashCode(), - selectedIcon = R.drawable.round_reports, - unselectedIcon = R.drawable.outline_reports, - onClick = { - navController.navigate(OrderScreenDestination()) - } - ) - ) - - val index = navItems.indexOf(navItems.find { it.index == currentRoute }) - - AnimatedNavigationBar( - modifier = Modifier - .windowInsetsPadding(windowInsets) - .height(80.dp), - selectedIndex = index, - cornerRadius = shapeCornerRadius(0.dp), - barColor = LightColor8, - ballColor = MaterialTheme.colorScheme.secondary, - ballAnimation = Teleport(tween(Duration, easing = LinearOutSlowInEasing)), - indentAnimation = Height( - indentWidth = 56.dp, - indentHeight = 15.dp, - animationSpec = tween( - DoubleDuration, - easing = { OvershootInterpolator().getInterpolation(it) } - ) - ) - ) { - navItems.forEach { - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - DropletButton( - modifier = Modifier.fillMaxWidth(), - isSelected = it.selected, - onClick = it.onClick, - icon = if (it.selected) it.selectedIcon else it.unselectedIcon, - dropletColor = Purple, - iconColor = MaterialTheme.colorScheme.tertiary, - size = 24.dp, - animationSpec = tween(durationMillis = Duration, easing = LinearEasing) - ) - - Spacer(modifier = Modifier.height(3.dp)) - - Text( - text = it.name, - color = if (it.selected) Purple else MaterialTheme.colorScheme.tertiary, - style = MaterialTheme.typography.labelSmall, - fontWeight = if (it.selected) FontWeight.SemiBold else FontWeight.Normal, - ) - } - } - } - -} - - -@Composable -internal fun containerColor(colorTransitionFraction: Float): Color { - return lerp( - MaterialTheme.colorScheme.background, - MaterialTheme.colorScheme.surfaceVariant, - FastOutLinearInEasing.transform(colorTransitionFraction) - ) -} - -@Composable -internal fun containerShape(colorTransitionFraction: Float): Shape { - val data = lerp( - CornerRadius(48f, 48f), - CornerRadius(0f, 0f), - FastOutLinearInEasing.transform(colorTransitionFraction) - ) - - return RoundedCornerShape(data.x, data.y) -} - -@Stable -data class NavigationItem( - val index: Int, - val name: String, - val selected: Boolean, - val selectedIcon: Int, - val unselectedIcon: Int, - val onClick: () -> Unit, -) - -const val Duration = 500 -const val DoubleDuration = 1000 \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/BellColorButton.kt b/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/BellColorButton.kt deleted file mode 100644 index d7b7bd3a..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/BellColorButton.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.niyaj.poposroom.features.common.components.color_buttons - -import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.tween -import androidx.compose.material3.Icon -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.Color.Companion.LightGray -import androidx.compose.ui.res.painterResource -import com.exyte.animatednavbar.utils.rotationWithTopCenterAnchor -import kotlin.math.PI -import kotlin.math.sin - -class BellColorButton( - override val animationSpec: FiniteAnimationSpec = tween(), - override val background: ButtonBackground, - private val maxDegrees: Float = 30f, -) : ColorButtonAnimation(animationSpec, background) { - - @Composable - override fun AnimatingIcon( - modifier: Modifier, - isSelected: Boolean, - isFromLeft: Boolean, - icon: Int, - ) { - val rotationFraction = animateFloatAsState( - targetValue = if (isSelected) 1f else 0f, - animationSpec = animationSpec, - label = "rotationFractionAnimation" - ) - - val color = animateColorAsState( - targetValue = if (isSelected) Color.Black else LightGray, - label = "colorAnimation" - ) - - Icon( - modifier = modifier - .rotationWithTopCenterAnchor( - if (isSelected) degreesRotationInterpolation( - maxDegrees, - rotationFraction.value - ) else 0f - ), - painter = painterResource(id = icon), - contentDescription = null, - tint = color.value - ) - } - - private fun degreesRotationInterpolation(maxDegrees: Float, fraction: Float) = - sin(fraction * 2 * PI).toFloat() * maxDegrees -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/BottomBarButton.kt b/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/BottomBarButton.kt deleted file mode 100644 index d2c69993..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/BottomBarButton.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.niyaj.poposroom.features.common.components.color_buttons - -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.compose.animation.core.tween -import androidx.compose.runtime.Stable -import com.niyaj.poposroom.R - -@Stable -data class WiggleButtonItem( - @DrawableRes val backgroundIcon: Int, - @DrawableRes val icon: Int, - var isSelected: Boolean, - @StringRes val description: Int, - val animationType: ColorButtonAnimation = BellColorButton( - tween(500), - background = ButtonBackground(R.drawable.plus) - ), -) - -@Stable -data class Item( - @DrawableRes val icon: Int, - var isSelected: Boolean, - @StringRes val description: Int, - val animationType: ColorButtonAnimation = BellColorButton( - tween(500), - background = ButtonBackground(R.drawable.plus) - ), -) \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/CalenderColorButton.kt b/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/CalenderColorButton.kt deleted file mode 100644 index 1d58d356..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/CalenderColorButton.kt +++ /dev/null @@ -1,126 +0,0 @@ -package com.niyaj.poposroom.features.common.components.color_buttons - -import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.Icon -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import com.exyte.animatednavbar.utils.toPxf -import com.niyaj.poposroom.features.common.ui.theme.LightGrey - - -data class CalendarAnimation( - override val animationSpec: FiniteAnimationSpec, - override val background: ButtonBackground, -) : ColorButtonAnimation(animationSpec, background) { - - @Composable - override fun AnimatingIcon( - modifier: Modifier, - isSelected: Boolean, - isFromLeft: Boolean, - icon: Int, - ) { - - Box( - modifier = modifier - ) { - val fraction = animateFloatAsState( - targetValue = if (isSelected) 1f else 0f, - animationSpec = animationSpec, - label = "fractionAnimation" - ) - - val layoutDirection = LocalLayoutDirection.current - val isLeftAnimation = remember(isFromLeft) { - if (layoutDirection == LayoutDirection.Ltr) { - isFromLeft - } else { - !isFromLeft - } - } - - val color = animateColorAsState( - targetValue = if (isSelected) Color.Black else LightGrey, - label = "colorAnimation" - ) - - Icon( - modifier = Modifier - .align(Alignment.Center) - .graphicsLayer( - translationX = if (isSelected) offset( - 10f, - fraction.value, - isLeftAnimation - ) else 0f - ), - painter = painterResource(id = icon), - contentDescription = null, - tint = color.value - ) - - CalendarPoint( - modifier = Modifier.align(Alignment.Center), - offsetX = if (isSelected) offset( - 15f, - fraction.value, - isLeftAnimation - ) else 0f, - iconColor = color.value - ) - } - } - - private fun offset(maxHorizontalOffset: Float, fraction: Float, isFromLeft: Boolean): Float { - val maxOffset = if (isFromLeft) -maxHorizontalOffset else maxHorizontalOffset - return if (fraction < 0.5) { - 2 * fraction * maxOffset - } else { - 2 * (1 - fraction) * maxOffset - } - } -} - -@Composable -fun CalendarPoint( - modifier: Modifier = Modifier, - offsetX: Float, - iconColor: Color, -) { - val layoutDirection = LocalLayoutDirection.current - val density = LocalDensity.current - val internalOffset = remember { - if (layoutDirection == LayoutDirection.Ltr) { - Offset(3.5.dp.toPxf(density), 5.dp.toPxf(density)) - } else { - Offset((-3.5).dp.toPxf(density), 5.dp.toPxf(density)) - } - } - Box( - modifier = modifier - .graphicsLayer( - translationX = internalOffset.x + offsetX, - translationY = internalOffset.y - ) - .size(3.dp) - .clip(CircleShape) - .background(iconColor) - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/ColorButton.kt b/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/ColorButton.kt deleted file mode 100644 index 017864fd..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/ColorButton.kt +++ /dev/null @@ -1,122 +0,0 @@ -package com.niyaj.poposroom.features.common.components.color_buttons - -import androidx.annotation.DrawableRes -import androidx.compose.animation.core.AnimationSpec -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.LinearEasing -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.animation.core.tween -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.offset -import androidx.compose.runtime.Composable -import androidx.compose.runtime.Stable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.scale -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.DpOffset -import androidx.compose.ui.unit.dp -import com.exyte.animatednavbar.utils.lerp -import com.exyte.animatednavbar.utils.noRippleClickable -import com.exyte.animatednavbar.utils.toDp -import com.exyte.animatednavbar.utils.toPxf - -data class ButtonBackground( - @DrawableRes val icon: Int, - val offset: DpOffset = DpOffset.Zero -) - -@Stable -abstract class ColorButtonAnimation( - open val animationSpec: FiniteAnimationSpec = tween(10000), - open val background: ButtonBackground, -) { - @Composable - abstract fun AnimatingIcon( - modifier: Modifier, - isSelected: Boolean, - isFromLeft: Boolean, - icon: Int, - ) -} - -@Composable -fun ColorButton( - modifier: Modifier = Modifier, - index: Int, - selectedIndex: Int, - prevSelectedIndex: Int, - onClick: () -> Unit, - @DrawableRes icon: Int, - contentDescription: String? = null, - background: ButtonBackground, - backgroundAnimationSpec: AnimationSpec = remember { tween(300, easing = LinearEasing) }, - animationType: ColorButtonAnimation, - maxBackgroundOffset: Dp = 25.dp -) { - - Box( - modifier = modifier.noRippleClickable { onClick() } - ) { - val isSelected = remember(selectedIndex, index) { selectedIndex == index } - - val fraction = animateFloatAsState( - targetValue = if (isSelected) 1f else 0f, - animationSpec = backgroundAnimationSpec, - label = "fractionAnimation", - ) - - val density = LocalDensity.current - val maxOffset = remember(maxBackgroundOffset) { maxBackgroundOffset.toPxf(density) } - - val isFromLeft = remember(prevSelectedIndex, index, selectedIndex) { - (prevSelectedIndex < index) || (selectedIndex > index) - } - val offset by remember(isSelected, isFromLeft) { - derivedStateOf { - calculateBackgroundOffset( - isSelected = isSelected, - isFromLeft = isFromLeft, - maxOffset = maxOffset, - fraction = fraction.value - ) - } - } - - Image( - modifier = Modifier - .offset(x = background.offset.x + offset.toDp(), y = background.offset.y) - .scale(fraction.value) - .align(Alignment.Center), - painter = painterResource(id = background.icon), - contentDescription = contentDescription - ) - - animationType.AnimatingIcon( - modifier = Modifier.align(Alignment.Center), - isSelected = isSelected, - isFromLeft = isFromLeft, - icon = icon, - ) - } -} - -private fun calculateBackgroundOffset( - isSelected: Boolean, - isFromLeft: Boolean, - fraction: Float, - maxOffset: Float -): Float { - val offset = if (isFromLeft) -maxOffset else maxOffset - return if (isSelected) { - lerp(offset, 0f, fraction) - } else { - lerp(-offset, 0f, fraction) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/GearColorButton.kt b/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/GearColorButton.kt deleted file mode 100644 index 1aa1e511..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/GearColorButton.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.niyaj.poposroom.features.common.components.color_buttons - -import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.material.Icon -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.rotate -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.LayoutDirection -import com.niyaj.poposroom.features.common.ui.theme.LightGrey - -class GearColorButton( - override val animationSpec: FiniteAnimationSpec, - override val background: ButtonBackground, - private val maxGearAnimationDegree: Float = 50f, -) : ColorButtonAnimation(animationSpec, background) { - - @Composable - override fun AnimatingIcon( - modifier: Modifier, - isSelected: Boolean, - isFromLeft: Boolean, - icon: Int, - ) { - val layoutDirection = LocalLayoutDirection.current - val gearAnimationDegree = remember { - if (layoutDirection == LayoutDirection.Ltr) { - maxGearAnimationDegree - } else { - -maxGearAnimationDegree - } - } - val degree = animateFloatAsState( - targetValue = if (isSelected) gearAnimationDegree else 0f, - animationSpec = animationSpec, - label = "degreeAnimation" - ) - - val color = animateColorAsState( - targetValue = if (isSelected) Color.Black else LightGrey, - label = "colorAnimation" - ) - - Icon( - modifier = modifier - .rotate(if (isSelected) degree.value else 0f), - painter = painterResource(id = icon), - contentDescription = null, - tint = color.value - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/PlusColorButton.kt b/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/PlusColorButton.kt deleted file mode 100644 index a7b32070..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/color_buttons/PlusColorButton.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.niyaj.poposroom.features.common.components.color_buttons - -import androidx.compose.animation.animateColorAsState -import androidx.compose.animation.core.FiniteAnimationSpec -import androidx.compose.animation.core.animateFloatAsState -import androidx.compose.foundation.layout.Box -import androidx.compose.material.Icon -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.rotate -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.unit.LayoutDirection -import com.niyaj.poposroom.R - -class PlusColorButton( - override val animationSpec: FiniteAnimationSpec, - override val background: ButtonBackground, -) : ColorButtonAnimation(animationSpec, background) { - - @Composable - override fun AnimatingIcon( - modifier: Modifier, - isSelected: Boolean, - isFromLeft: Boolean, - icon: Int, - ) { - Box( - modifier = modifier - ) { - val color = animateColorAsState( - targetValue = if (isSelected) Color.Black else Color.LightGray, - label = "colorAnimation" - ) - - Icon( - modifier = Modifier - .align(Alignment.Center), - painter = painterResource(id = R.drawable.rounded_rect), - contentDescription = null, - tint = color.value - ) - - val layoutDirection = LocalLayoutDirection.current - val requiredDegrees = remember(isFromLeft) { - val rotateDegrees = if (isFromLeft) ROTATE_DEGREES else -ROTATE_DEGREES - if (layoutDirection == LayoutDirection.Rtl) -rotateDegrees else rotateDegrees - } - - - val degrees = animateFloatAsState( - targetValue = if (isSelected) requiredDegrees else 0f, - animationSpec = animationSpec, - label = "degreesAnimation" - ) - - Icon( - modifier = Modifier - .align(Alignment.Center) - .rotate(if (isSelected) degrees.value else 0f), - painter = painterResource(id = R.drawable.plus), - contentDescription = null, - tint = color.value - ) - } - } -} - -const val ROTATE_DEGREES = 90f \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/database/PoposDatabase.kt b/app/src/main/java/com/niyaj/poposroom/features/common/database/PoposDatabase.kt deleted file mode 100644 index c751bc35..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/database/PoposDatabase.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.niyaj.poposroom.features.common.database - -import androidx.room.Database -import androidx.room.RoomDatabase -import androidx.room.TypeConverters -import com.niyaj.poposroom.features.addon_item.data.dao.AddOnItemDao -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.address.data.dao.AddressDao -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.cart.data.dao.CartDao -import com.niyaj.poposroom.features.cart.domain.model.CartEntity -import com.niyaj.poposroom.features.cart_order.data.dao.CartOrderDao -import com.niyaj.poposroom.features.cart_order.domain.model.CartAddOnItems -import com.niyaj.poposroom.features.cart_order.domain.model.CartCharges -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrderEntity -import com.niyaj.poposroom.features.category.data.dao.CategoryDao -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.charges.data.dao.ChargesDao -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.common.database.utils.TimestampConverters -import com.niyaj.poposroom.features.customer.data.dao.CustomerDao -import com.niyaj.poposroom.features.customer.domain.model.Customer -import com.niyaj.poposroom.features.employee.data.dao.EmployeeDao -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee_absent.data.dao.AbsentDao -import com.niyaj.poposroom.features.employee_absent.domain.model.Absent -import com.niyaj.poposroom.features.employee_absent.domain.model.EmployeeWithAbsentCrossRef -import com.niyaj.poposroom.features.employee_payment.data.dao.PaymentDao -import com.niyaj.poposroom.features.employee_payment.domain.model.EmployeeWithPaymentCrossRef -import com.niyaj.poposroom.features.employee_payment.domain.model.Payment -import com.niyaj.poposroom.features.expenses.data.dao.ExpenseDao -import com.niyaj.poposroom.features.expenses.domain.model.Expense -import com.niyaj.poposroom.features.main_feed.data.dao.MainFeedDao -import com.niyaj.poposroom.features.order.data.dao.OrderDao -import com.niyaj.poposroom.features.product.data.dao.ProductDao -import com.niyaj.poposroom.features.product.domain.model.CategoryWithProductCrossRef -import com.niyaj.poposroom.features.product.domain.model.Product -import com.niyaj.poposroom.features.selected.data.dao.SelectedDao -import com.niyaj.poposroom.features.selected.domain.model.Selected - -@Database( - entities = [ - AddOnItem::class, - Address::class, - Charges::class, - Category::class, - Customer::class, - Employee::class, - Payment::class, - EmployeeWithPaymentCrossRef::class, - Absent::class, - EmployeeWithAbsentCrossRef::class, - Expense::class, - Product::class, - CategoryWithProductCrossRef::class, - CartOrderEntity::class, - CartAddOnItems::class, - CartCharges::class, - Selected::class, - CartEntity::class, - ], - version = 1, - autoMigrations = [], - exportSchema = true, -) -@TypeConverters(TimestampConverters::class) -abstract class PoposDatabase : RoomDatabase() { - abstract fun addOnItemDao(): AddOnItemDao - abstract fun addressDao(): AddressDao - abstract fun chargesDao(): ChargesDao - abstract fun categoryDao(): CategoryDao - abstract fun customerDao(): CustomerDao - abstract fun employeeDao(): EmployeeDao - abstract fun paymentDao(): PaymentDao - abstract fun absentDao(): AbsentDao - abstract fun expenseDao(): ExpenseDao - abstract fun productDao(): ProductDao - abstract fun cartOrderDao(): CartOrderDao - abstract fun selectedDao(): SelectedDao - abstract fun mainFeedDao(): MainFeedDao - abstract fun cartDao(): CartDao - abstract fun orderDao(): OrderDao -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/event/UiState.kt b/app/src/main/java/com/niyaj/poposroom/features/common/event/UiState.kt deleted file mode 100644 index 317ffc14..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/event/UiState.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.niyaj.poposroom.features.common.event - -sealed interface UiState { - object Loading: UiState - object Empty: UiState - data class Success(val data: T) : UiState -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/navigation/PoposNavigation.kt b/app/src/main/java/com/niyaj/poposroom/features/common/navigation/PoposNavigation.kt deleted file mode 100644 index 904c3992..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/navigation/PoposNavigation.kt +++ /dev/null @@ -1,107 +0,0 @@ -package com.niyaj.poposroom.features.common.navigation - -import androidx.compose.material.ScaffoldState -import androidx.compose.material3.BottomSheetScaffoldState -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.SnackbarHostState -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavHostController -import com.niyaj.poposroom.features.NavGraphs -import com.niyaj.poposroom.features.addon_item.presentation.AddOnItemScreen -import com.niyaj.poposroom.features.address.presentation.AddressScreen -import com.niyaj.poposroom.features.category.presentation.CategoryScreen -import com.niyaj.poposroom.features.charges.presentation.ChargesScreen -import com.niyaj.poposroom.features.common.utils.SheetScreen -import com.niyaj.poposroom.features.customer.presentaion.CustomerScreen -import com.niyaj.poposroom.features.destinations.AddOnItemScreenDestination -import com.niyaj.poposroom.features.destinations.AddressScreenDestination -import com.niyaj.poposroom.features.destinations.CategoryScreenDestination -import com.niyaj.poposroom.features.destinations.ChargesScreenDestination -import com.niyaj.poposroom.features.destinations.CustomerScreenDestination -import com.ramcosta.composedestinations.DestinationsNavHost -import com.ramcosta.composedestinations.manualcomposablecalls.composable -import com.ramcosta.composedestinations.navigation.dependency -import com.ramcosta.composedestinations.rememberNavHostEngine -import com.ramcosta.composedestinations.spec.Route - -/** - * Navigation controller - * @author Sk Niyaj Ali - * @param modifier - * @param scaffoldState - * @param navController - * @param startRoute - */ -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun PoposNavigation( - modifier: Modifier = Modifier, - bottomSheetScaffoldState: BottomSheetScaffoldState, - scaffoldState: ScaffoldState, - snackbarState: SnackbarHostState, - navController: NavHostController, - startRoute: Route, - closeSheet: () -> Unit, - onOpenSheet: (SheetScreen) -> Unit, -) { - - val navHostEngine = rememberNavHostEngine() - - DestinationsNavHost( - modifier = modifier, - navGraph = NavGraphs.root, - navController = navController, - startRoute = startRoute, - engine = navHostEngine, - dependenciesContainerBuilder = { - dependency(scaffoldState) - dependency(bottomSheetScaffoldState) - dependency(snackbarState) - }, - ) { - composable(AddOnItemScreenDestination) { - AddOnItemScreen( - bottomSheetScaffoldState = bottomSheetScaffoldState, - navController = navController, - onCloseSheet = closeSheet, - onOpenSheet = onOpenSheet, - ) - } - - composable(AddressScreenDestination) { - AddressScreen( - bottomSheetScaffoldState = bottomSheetScaffoldState, - navController = navController, - onCloseSheet = closeSheet, - onOpenSheet = onOpenSheet, - ) - } - - composable(ChargesScreenDestination) { - ChargesScreen( - bottomSheetScaffoldState = bottomSheetScaffoldState, - navController = navController, - onCloseSheet = closeSheet, - onOpenSheet = onOpenSheet, - ) - } - - composable(CategoryScreenDestination) { - CategoryScreen( - bottomSheetScaffoldState = bottomSheetScaffoldState, - navController = navController, - onCloseSheet = closeSheet, - onOpenSheet = onOpenSheet, - ) - } - composable(CustomerScreenDestination) { - CustomerScreen( - bottomSheetScaffoldState = bottomSheetScaffoldState, - navController = navController, - onCloseSheet = closeSheet, - onOpenSheet = onOpenSheet, - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/navigation/Screen.kt b/app/src/main/java/com/niyaj/poposroom/features/common/navigation/Screen.kt deleted file mode 100644 index 528fcdc5..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/navigation/Screen.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.niyaj.poposroom.features.common.navigation - -sealed class Screen(val route: String) { - - object SplashScreen: Screen("splash_screen") - - object LoginScreen : Screen("login_screen") - - object MainFeedScreen : Screen("main_feed_screen") - - object ProfileScreen : Screen("profile_screen") - - object CartScreen : Screen("cart_screen") - - object OrderScreen : Screen("order_screen") - - object OrderDetailsScreen : Screen("order_details_screen") - - object NotificationScreen: Screen("notification") - - object SearchScreen : Screen("search_screen") - - object SettingsScreen : Screen("settings_screen") - - object PrintSettingsScreen : Screen("print_settings_screen") - - object CategoryScreen : Screen("category_screen") - - object ProductsScreen : Screen("products_screen") - - object AddressScreen : Screen("address_screen") - - object CustomerScreen : Screen("customer_screen") - - object CartOrderScreen : Screen("cart_order_screen") - - object CartOrderSettingsScreen : Screen("cart_order_settings_screen") - - object AddOnItemScreen : Screen("add_on_items_screen") - - object ChargesScreen : Screen("charges_screen") - - object PartnerScreen : Screen("partner_screen") - - object AddEditPartnerScreen : Screen("add_edit_partner_screen") - - object EmployeeScreen : Screen("employee_screen") - - object AddEditEmployeeScreen : Screen("add_edit_employee_screen") - - object ExpensesCategoryScreen : Screen("expenses_category_screen") - - object ExpensesSubScreen : Screen("expenses_sub_category_screen") - - object ExpensesScreen : Screen("expenses_screen") - - object AddExpensesScreen : Screen("add_expenses_screen") - - object EditExpensesScreen : Screen("edit_expenses_screen") - - object ReportScreen : Screen("report_screen") - - object ViewLastSevenDaysReports: Screen("view_last_seven_days_reports") - -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Theme.kt b/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Theme.kt deleted file mode 100644 index 1d396376..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Theme.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.niyaj.poposroom.features.common.ui.theme - -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable - - -private val LightColors = lightColorScheme( - primary = light_primary, - onPrimary = light_onPrimary, - primaryContainer = light_primaryContainer, - onPrimaryContainer = light_onPrimaryContainer, - secondary = light_secondary, - onSecondary = light_onSecondary, - secondaryContainer = light_secondaryContainer, - onSecondaryContainer = light_onSecondaryContainer, - tertiary = light_tertiary, - onTertiary = light_onTertiary, - tertiaryContainer = light_tertiaryContainer, - onTertiaryContainer = light_onTertiaryContainer, - error = light_error, - errorContainer = light_errorContainer, - onError = light_onError, - onErrorContainer = light_onErrorContainer, - background = light_background, - onBackground = light_onBackground, - surface = light_surface, - onSurface = light_onSurface, - surfaceVariant = light_surfaceVariant, - onSurfaceVariant = light_onSurfaceVariant, - outline = light_outline, - inverseOnSurface = light_inverseOnSurface, - inverseSurface = light_inverseSurface, - inversePrimary = light_inversePrimary, - surfaceTint = light_surfaceTint, - outlineVariant = light_outlineVariant, - scrim = light_scrim, -) - - -private val DarkColors = darkColorScheme( - primary = dark_primary, - onPrimary = dark_onPrimary, - primaryContainer = dark_primaryContainer, - onPrimaryContainer = dark_onPrimaryContainer, - secondary = dark_secondary, - onSecondary = dark_onSecondary, - secondaryContainer = dark_secondaryContainer, - onSecondaryContainer = dark_onSecondaryContainer, - tertiary = dark_tertiary, - onTertiary = dark_onTertiary, - tertiaryContainer = dark_tertiaryContainer, - onTertiaryContainer = dark_onTertiaryContainer, - error = dark_error, - errorContainer = dark_errorContainer, - onError = dark_onError, - onErrorContainer = dark_onErrorContainer, - background = dark_background, - onBackground = dark_onBackground, - surface = dark_surface, - onSurface = dark_onSurface, - surfaceVariant = dark_surfaceVariant, - onSurfaceVariant = dark_onSurfaceVariant, - outline = dark_outline, - inverseOnSurface = dark_inverseOnSurface, - inverseSurface = dark_inverseSurface, - inversePrimary = dark_inversePrimary, - surfaceTint = dark_surfaceTint, - outlineVariant = dark_outlineVariant, - scrim = dark_scrim, -) - -@Composable -fun PoposRoomTheme( - useDarkTheme: Boolean = isSystemInDarkTheme(), - content: @Composable() () -> Unit -) { - val colors = if (!useDarkTheme) { - LightColors - } else { - DarkColors - } - - MaterialTheme( - colorScheme = colors, - typography = Typography, - shapes = Shapes, - content = content - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/utils/Constants.kt b/app/src/main/java/com/niyaj/poposroom/features/common/utils/Constants.kt deleted file mode 100644 index 9f54428b..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/utils/Constants.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.niyaj.poposroom.features.common.utils - -object Constants { - const val SPLASH_SCREEN_DURATION = 100L - - const val PRINTER_DPI = 176 - - const val PRINTER_WIDTH_MM = 58f - - const val PRINTER_NBR_LINE = 31 - - const val PAYMENT_QR_DATA = "upi://pay?pa=paytmqr281005050101zry6uqipmngr@paytm&pn=Paytm%20Merchant&paytmqr=281005050101ZRY6UQIPMNGR" - - const val PRODUCT_NAME_LENGTH = 18 - - const val PAID = "Paid" - - const val NOT_PAID = "Not Paid" - - - const val TEXT_FIELD_LEADING_ICON = "TextFieldLeadingIcon" - const val TEXT_FIELD_TRAILING_ICON = "TextFieldLeadingIcon" - - const val SEARCH_ITEM_NOT_FOUND = "Searched Item Not Found." - const val SEARCH_ITEM_PLACEHOLDER = "Search for items..." - - const val STANDARD_BACK_BUTTON = "StandardBackButton" - const val SEARCH_BAR_CLEAR_BUTTON = "SearchBarClearButton" - const val STANDARD_SEARCH_BAR = "StandardSearchBar" - const val FAB_TEXT = "Create New" - const val LOADING_INDICATION = "loadingIndicator" - const val SEARCH_ICON = "SearchIcon" - const val SETTINGS_ICON = "SettingsIcon" - const val EDIT_ICON = "EditIcon" - const val DELETE_ICON = "DeleteIcon" - const val CLEAR_ICON = "ClearIcon" - const val SELECTALL_ICON = "SelectAllIcon" - - - const val JSON_FILE_TYPE = "application/json" - - const val JSON_FILE_EXTENSION = ".json" - - const val SAVABLE_FILE_NAME = "popos" -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/utils/PoposDispatchers.kt b/app/src/main/java/com/niyaj/poposroom/features/common/utils/PoposDispatchers.kt deleted file mode 100644 index 86aa948b..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/utils/PoposDispatchers.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.niyaj.poposroom.features.common.utils - -import javax.inject.Qualifier - -@Qualifier -@Retention(AnnotationRetention.RUNTIME) -annotation class Dispatcher(val poposDispatcher: PoposDispatchers) - -enum class PoposDispatchers { - Default, - IO, -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/utils/SheetLayout.kt b/app/src/main/java/com/niyaj/poposroom/features/common/utils/SheetLayout.kt deleted file mode 100644 index 94803602..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/utils/SheetLayout.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.niyaj.poposroom.features.common.utils - -import androidx.compose.runtime.Composable -import com.niyaj.poposroom.features.addon_item.presentation.add_edit.AddEditItemScreen -import com.niyaj.poposroom.features.address.presentation.add_edit.AddEditAddressScreen -import com.niyaj.poposroom.features.category.presentation.add_edit.AddEditCategoryScreen -import com.niyaj.poposroom.features.charges.presentation.add_edit.AddEditChargesScreen -import com.niyaj.poposroom.features.customer.presentaion.add_edit.AddEditCustomerScreen - -@Composable -fun SheetLayout( - current: SheetScreen, - onCloseBottomSheet: () -> Unit, -) { - BottomSheetWithCloseDialog( - text = current.type, - onClosePressed = onCloseBottomSheet - ) { - when (current) { - is SheetScreen.CreateNewAddOnItem -> { - AddEditItemScreen(closeSheet = onCloseBottomSheet) - } - - is SheetScreen.UpdateAddOnItem -> { - AddEditItemScreen(addOnItemId = current.itemId, closeSheet = onCloseBottomSheet) - } - - is SheetScreen.CreateNewAddress -> { - AddEditAddressScreen(closeSheet = onCloseBottomSheet) - } - - is SheetScreen.UpdateAddress -> { - AddEditAddressScreen( - addressId = current.addressId, - closeSheet = onCloseBottomSheet - ) - } - - is SheetScreen.CreateNewCharges -> { - AddEditChargesScreen(closeSheet = onCloseBottomSheet) - } - - is SheetScreen.UpdateCharges -> { - AddEditChargesScreen( - chargesId = current.chargesId, - closeSheet = onCloseBottomSheet - ) - } - - is SheetScreen.CreateNewCategory -> { - AddEditCategoryScreen(closeSheet = onCloseBottomSheet) - } - - is SheetScreen.UpdateCategory -> { - AddEditCategoryScreen( - categoryId = current.categoryId, - closeSheet = onCloseBottomSheet - ) - } - is SheetScreen.CreateNewCustomer -> { - AddEditCustomerScreen(closeSheet = onCloseBottomSheet) - } - - is SheetScreen.UpdateCustomer -> { - AddEditCustomerScreen( - customerId = current.customerId, - closeSheet = onCloseBottomSheet - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/utils/UiEvent.kt b/app/src/main/java/com/niyaj/poposroom/features/common/utils/UiEvent.kt deleted file mode 100644 index 85ac75f3..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/common/utils/UiEvent.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.niyaj.poposroom.features.common.utils - -sealed class UiEvent{ - data class IsLoading(val isLoading: Boolean? = false) : UiEvent() - data class OnSuccess(val successMessage: String) : UiEvent() - data class OnError(val errorMessage: String): UiEvent() -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerScreen.kt b/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerScreen.kt deleted file mode 100644 index 3e9dcaa1..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerScreen.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.niyaj.poposroom.features.customer.presentaion.add_edit - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material.icons.filled.Edit -import androidx.compose.material.icons.filled.Email -import androidx.compose.material.icons.filled.Person -import androidx.compose.material.icons.filled.PhoneAndroid -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.text.input.KeyboardType -import androidx.hilt.navigation.compose.hiltViewModel -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardTextField -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.ADD_EDIT_CUSTOMER_BUTTON -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.ADD_EDIT_CUSTOMER_SCREEN -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CREATE_NEW_CUSTOMER -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_EMAIL_ERROR -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_EMAIL_FIELD -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_NAME_ERROR -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_NAME_FIELD -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_PHONE_ERROR -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_PHONE_FIELD -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.EDIT_CUSTOMER_ITEM - -@Composable -fun AddEditCustomerScreen( - customerId: Int = 0, - closeSheet: () -> Unit, - viewModel: AddEditCustomerViewModel = hiltViewModel(), -) { - val phoneError = viewModel.phoneError.collectAsStateWithLifecycle().value - val nameError = viewModel.nameError.collectAsStateWithLifecycle().value - val emailError = viewModel.emailError.collectAsStateWithLifecycle().value - - val enableBtn = phoneError == null && nameError == null && emailError == null - - val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value - - LaunchedEffect(key1 = Unit) { - viewModel.resetFields() - } - - LaunchedEffect(key1 = event) { - event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} - is UiEvent.OnError -> { - closeSheet() - } - is UiEvent.OnSuccess -> { - closeSheet() - } - } - } - } - - LaunchedEffect(key1 = customerId) { - viewModel.getCustomerById(customerId) - } - - Column( - modifier = Modifier - .testTag(ADD_EDIT_CUSTOMER_SCREEN) - .fillMaxWidth(), - verticalArrangement = Arrangement.Center, - ) { - StandardTextField( - value = viewModel.addEditState.customerPhone, - label = CUSTOMER_PHONE_FIELD, - leadingIcon = Icons.Default.PhoneAndroid, - isError = phoneError != null, - errorText = phoneError, - errorTextTag = CUSTOMER_PHONE_ERROR, - keyboardType = KeyboardType.Number, - onValueChange = { - viewModel.onEvent(AddEditCustomerEvent.CustomerPhoneChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardTextField( - value = viewModel.addEditState.customerName ?: "", - label = CUSTOMER_NAME_FIELD, - leadingIcon = Icons.Default.Person, - isError = nameError != null, - errorText = nameError, - errorTextTag = CUSTOMER_NAME_ERROR, - onValueChange = { - viewModel.onEvent(AddEditCustomerEvent.CustomerNameChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardTextField( - value = viewModel.addEditState.customerEmail ?: "", - label = CUSTOMER_EMAIL_FIELD, - leadingIcon = Icons.Default.Email, - isError = emailError != null, - errorText = emailError, - errorTextTag = CUSTOMER_EMAIL_ERROR, - onValueChange = { - viewModel.onEvent(AddEditCustomerEvent.CustomerEmailChanged(it)) - } - ) - - Spacer(modifier = Modifier.height(SpaceSmall)) - - StandardButton( - modifier = Modifier.testTag(ADD_EDIT_CUSTOMER_BUTTON), - text = if (customerId == 0) CREATE_NEW_CUSTOMER else EDIT_CUSTOMER_ITEM, - icon = if (customerId == 0) Icons.Default.Add else Icons.Default.Edit, - enabled = enableBtn, - onClick = { - viewModel.onEvent(AddEditCustomerEvent.CreateOrUpdateCustomer(customerId)) - } - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/data/use_cases/GetPagingExpenses.kt b/app/src/main/java/com/niyaj/poposroom/features/expenses/data/use_cases/GetPagingExpenses.kt deleted file mode 100644 index c41f4bb2..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/data/use_cases/GetPagingExpenses.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.niyaj.poposroom.features.expenses.data.use_cases - -import androidx.paging.PagingSource -import androidx.paging.PagingState -import com.niyaj.poposroom.features.expenses.domain.model.Expense -import com.niyaj.poposroom.features.expenses.domain.repository.ExpenseRepository - -class GetPagingExpenses( - private val expenseRepository: ExpenseRepository, - private val searchText: String, -): PagingSource() { - override fun getRefreshKey(state: PagingState): Int? { - return state.anchorPosition?.let { anchorPosition -> - val anchorPage = state.closestPageToPosition(anchorPosition) - anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1) - } - } - - override suspend fun load(params: LoadParams): LoadResult = - try { - val page = params.key ?: 0 - val size = params.loadSize - val from = page * size - val data = expenseRepository.getAllPagingExpenses(searchText = searchText, limit = size, offset = from) - - if (params.placeholdersEnabled) { - val itemsAfter = data.count() - from + data.size - LoadResult.Page( - data = data, - prevKey = if (page == 0) null else page - 1, - nextKey = if (data.isEmpty()) null else page + 1, - itemsAfter = if (itemsAfter > size) size else itemsAfter, - itemsBefore = from - ) - } else { - LoadResult.Page( - data = data, - prevKey = if (page == 0) null else page - 1, - nextKey = if (data.isEmpty()) null else page + 1 - ) - } - } catch (e: Exception) { - LoadResult.Error(e) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/main_feed/di/MainFeedModule.kt b/app/src/main/java/com/niyaj/poposroom/features/main_feed/di/MainFeedModule.kt deleted file mode 100644 index 1ac9df67..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/main_feed/di/MainFeedModule.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.niyaj.poposroom.features.main_feed.di - -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.main_feed.data.dao.MainFeedDao -import com.niyaj.poposroom.features.main_feed.data.repository.MainFeedRepositoryImpl -import com.niyaj.poposroom.features.main_feed.domain.repository.MainFeedRepository -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.CoroutineDispatcher - -@Module -@InstallIn(SingletonComponent::class) -object MainFeedModule { - - @Provides - fun provideMainFeedDao(database: PoposDatabase) : MainFeedDao { - return database.mainFeedDao() - } - - @Provides - fun provideMainFeedRepository( - mainFeedDao: MainFeedDao, - @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, - ): MainFeedRepository { - return MainFeedRepositoryImpl(mainFeedDao, ioDispatcher) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/main_feed/domain/repository/MainFeedRepository.kt b/app/src/main/java/com/niyaj/poposroom/features/main_feed/domain/repository/MainFeedRepository.kt deleted file mode 100644 index c01b3ea6..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/main_feed/domain/repository/MainFeedRepository.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.niyaj.poposroom.features.main_feed.domain.repository - -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.main_feed.domain.model.ProductWithFlowQuantity -import com.niyaj.poposroom.features.selected.domain.model.Selected -import kotlinx.coroutines.flow.Flow - -interface MainFeedRepository { - - fun getAllCategory(): Flow> - - suspend fun getAllProduct(searchText: String, selectedCategory: Int = 0): Flow> - - fun getSelectedOrder(): Flow -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/di/OrderModule.kt b/app/src/main/java/com/niyaj/poposroom/features/order/di/OrderModule.kt deleted file mode 100644 index b9f1035c..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/order/di/OrderModule.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.niyaj.poposroom.features.order.di - -import com.niyaj.poposroom.features.cart_order.data.dao.CartOrderDao -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.order.data.dao.OrderDao -import com.niyaj.poposroom.features.order.data.repository.OrderRepositoryImpl -import com.niyaj.poposroom.features.order.domain.repository.OrderRepository -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import kotlinx.coroutines.CoroutineDispatcher - -@Module -@InstallIn(SingletonComponent::class) -object OrderModule { - - @Provides - fun provideOrderDao(database: PoposDatabase) : OrderDao { - return database.orderDao() - } - - @Provides - fun provideOrderRepository( - orderDao: OrderDao, - cartOrderDao: CartOrderDao, - @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, - ): OrderRepository { - return OrderRepositoryImpl(orderDao, cartOrderDao, ioDispatcher) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/domain/model/Order.kt b/app/src/main/java/com/niyaj/poposroom/features/order/domain/model/Order.kt deleted file mode 100644 index 4d251b2d..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/order/domain/model/Order.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.niyaj.poposroom.features.order.domain.model - -import com.niyaj.poposroom.features.cart.domain.model.OrderPrice -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.utils.toTime -import java.util.Date - -data class Order( - val orderId: Int = 0, - val orderType: OrderType = OrderType.DineIn, - val customerPhone: String? = null, - val customerAddress: String? = null, - val orderDate: Date = Date(), - val orderPrice : OrderPrice = OrderPrice() -) - - -fun List.searchOrder(searchText: String): List { - return if (searchText.isNotEmpty()) { - this.filter { - it.orderId.toString().contains(searchText,true) || - it.orderType.name.contains(searchText,true) || - it.customerPhone?.contains(searchText,true) == true || - it.customerAddress?.contains(searchText,true) == true || - it.orderDate.toTime.contains(searchText,true) || - it.orderPrice.totalPrice.plus(it.orderPrice.discountPrice).toString() - .contains(searchText,true) - } - }else this -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/domain/model/OrderDetails.kt b/app/src/main/java/com/niyaj/poposroom/features/order/domain/model/OrderDetails.kt deleted file mode 100644 index 376b0214..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/order/domain/model/OrderDetails.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.niyaj.poposroom.features.order.domain.model - -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.cart.domain.model.CartProductItem -import com.niyaj.poposroom.features.cart.domain.model.OrderPrice -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrder -import com.niyaj.poposroom.features.charges.domain.model.Charges -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.emptyFlow - -data class OrderDetails( - val cartOrder: CartOrder = CartOrder(), - val cartProducts: List = emptyList(), - val addOnItems: Flow> = emptyFlow(), - val charges: Flow> = emptyFlow(), - val orderPrice: OrderPrice = OrderPrice() -) diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderState.kt b/app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderState.kt deleted file mode 100644 index 3fb22562..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderState.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.niyaj.poposroom.features.order.presentation - -import com.niyaj.poposroom.features.order.domain.model.Order - -data class OrderState( - val orders: List = emptyList(), - val isLoading: Boolean = true, -) diff --git a/app/src/main/java/com/niyaj/poposroom/features/selected/di/SelectedModule.kt b/app/src/main/java/com/niyaj/poposroom/features/selected/di/SelectedModule.kt deleted file mode 100644 index 77ee5d6b..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/selected/di/SelectedModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.niyaj.poposroom.features.selected.di - -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.selected.data.dao.SelectedDao -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent - -@Module -@InstallIn(SingletonComponent::class) -object SelectedModule { - - @Provides - fun provideSelectedDao(database: PoposDatabase) : SelectedDao { - return database.selectedDao() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/utils/ImportExport.kt b/app/src/main/java/com/niyaj/poposroom/features/utils/ImportExport.kt deleted file mode 100644 index f7f6f76b..00000000 --- a/app/src/main/java/com/niyaj/poposroom/features/utils/ImportExport.kt +++ /dev/null @@ -1,124 +0,0 @@ -package com.niyaj.poposroom.features.utils - -import android.content.Context -import android.content.Intent -import android.net.Uri -import android.os.Build -import android.provider.MediaStore -import androidx.annotation.RequiresApi -import com.niyaj.poposroom.features.common.utils.Constants.JSON_FILE_EXTENSION -import com.niyaj.poposroom.features.common.utils.Constants.JSON_FILE_TYPE -import com.niyaj.poposroom.features.common.utils.Constants.SAVABLE_FILE_NAME -import com.squareup.moshi.Moshi -import com.squareup.moshi.adapter -import kotlinx.coroutines.delay -import timber.log.Timber -import java.io.FileNotFoundException -import java.io.IOException - -object ImportExport { - - @RequiresApi(Build.VERSION_CODES.Q) - internal fun openFile( - context: Context, - pickerInitialUri: Uri = getUri(context), - ): Intent { - val intent = Intent( - Intent.ACTION_OPEN_DOCUMENT, - pickerInitialUri - ).apply { - type = JSON_FILE_TYPE - addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - addCategory(Intent.CATEGORY_OPENABLE) - } - - return intent - } - - @RequiresApi(Build.VERSION_CODES.Q) - internal fun createFile(context: Context, fileName: String = SAVABLE_FILE_NAME): Intent { - val intent = Intent( - Intent.ACTION_CREATE_DOCUMENT, - getUri(context) - ).apply { - type = JSON_FILE_TYPE - addCategory(Intent.CATEGORY_OPENABLE) - putExtra(Intent.EXTRA_TITLE, fileName.plus(JSON_FILE_EXTENSION)) - } - - return intent - } - - @OptIn(ExperimentalStdlibApi::class) - internal inline fun writeData(context: Context, uri: Uri, data: List): Boolean { - try { - - val moshi = Moshi.Builder().build().adapter>() - val list = moshi.toJson(data) - - context.applicationContext.contentResolver.openOutputStream(uri, "rwt")?.use { - it.flush() - it.bufferedWriter().use { writer -> - writer.write(list) - } - } - - return true - - } catch (e: FileNotFoundException) { - e.printStackTrace() - return false - } catch (e: IOException) { - e.printStackTrace() - return false - } - } - - - @OptIn(ExperimentalStdlibApi::class) - internal suspend inline fun readData(context: Context, uri: Uri): List { - try { - val container = mutableListOf() - - val moshi = Moshi.Builder().build().adapter>().nullSafe() - - context.applicationContext.contentResolver.openInputStream(uri)?.use { - it.bufferedReader().use { reader -> - delay(50L) - - moshi.fromJson(reader.readText())?.let { it1 -> container.addAll(it1) } - } - - delay(50L) - - it.close() - } - - return container.toList() - } catch (e: FileNotFoundException) { - Timber.e(e) - return emptyList() - } catch (e: IOException) { - Timber.e(e) - return emptyList() - } catch (e: Exception) { - Timber.e(e) - return emptyList() - } - } - - private fun getUri(context: Context): Uri { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - MediaStore.Downloads.INTERNAL_CONTENT_URI - } else { - getUriBelowQ(context) - } - } - - private fun getUriBelowQ(context: Context): Uri { - val result = context.filesDir - - return Uri.fromFile(result) - } -} diff --git a/app/src/main/java/com/niyaj/poposroom/navigation/PoposNavHost.kt b/app/src/main/java/com/niyaj/poposroom/navigation/PoposNavHost.kt new file mode 100644 index 00000000..a41b04ee --- /dev/null +++ b/app/src/main/java/com/niyaj/poposroom/navigation/PoposNavHost.kt @@ -0,0 +1,98 @@ +package com.niyaj.poposroom.navigation + +import androidx.compose.runtime.Composable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import com.niyaj.cart.CartScreen +import com.niyaj.cart.destinations.CartScreenDestination +import com.niyaj.cart_selected.SelectOrderScreen +import com.niyaj.cart_selected.destinations.SelectOrderScreenDestination +import com.niyaj.cartorder.CartOrderScreen +import com.niyaj.cartorder.destinations.AddEditCartOrderScreenDestination +import com.niyaj.cartorder.destinations.CartOrderScreenDestination +import com.niyaj.order.OrderScreen +import com.niyaj.order.destinations.OrderDetailsScreenDestination +import com.niyaj.order.destinations.OrderScreenDestination +import com.niyaj.poposroom.ui.PoposAppState +import com.ramcosta.composedestinations.DestinationsNavHost +import com.ramcosta.composedestinations.manualcomposablecalls.composable +import com.ramcosta.composedestinations.navigation.dependency +import com.ramcosta.composedestinations.navigation.navigate +import com.ramcosta.composedestinations.rememberNavHostEngine +import com.ramcosta.composedestinations.scope.resultBackNavigator +import com.ramcosta.composedestinations.scope.resultRecipient +import com.ramcosta.composedestinations.spec.Route + +/** + * Navigation controller + * @author Sk Niyaj Ali + * @param appState + * @param modifier + * @param startRoute + */ +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun PoposNavHost( + appState: PoposAppState, + modifier: Modifier = Modifier, + startRoute: Route, +) { + DestinationsNavHost( + engine = rememberNavHostEngine(), + modifier = modifier, + navController = appState.navController, + navGraph = RootNavGraph, + startRoute = startRoute, + dependenciesContainerBuilder = { + dependency(navController) + }, + manualComposableCallsBuilder = { + + composable(CartOrderScreenDestination) { + CartOrderScreen( + navController = navController, + onClickOrderDetails = { + navController.navigate(OrderDetailsScreenDestination(it)) + }, + resultRecipient = resultRecipient(), + ) + } + + composable(CartScreenDestination) { + CartScreen( + navController = navController, + onClickEditOrder = { + navController.navigate(AddEditCartOrderScreenDestination(it)) + }, + onClickOrderDetails = { + navController.navigate(OrderDetailsScreenDestination(it)) + }, + onNavigateToOrderScreen = { + navController.navigate(OrderScreenDestination()) + } + ) + } + + composable(OrderScreenDestination) { + OrderScreen( + navController = navController, + onClickEditOrder = { + navController.navigate(AddEditCartOrderScreenDestination(it)) + } + ) + } + + composable(SelectOrderScreenDestination) { + SelectOrderScreen( + navController = navController, + onEditClick = { + navController.navigate(AddEditCartOrderScreenDestination(it)) + }, + resultBackNavigator = resultBackNavigator() + ) + } + + + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/navigation/RootNavGraph.kt b/app/src/main/java/com/niyaj/poposroom/navigation/RootNavGraph.kt new file mode 100644 index 00000000..f8795c11 --- /dev/null +++ b/app/src/main/java/com/niyaj/poposroom/navigation/RootNavGraph.kt @@ -0,0 +1,52 @@ +package com.niyaj.poposroom.navigation + +import androidx.compose.ui.ExperimentalComposeUiApi +import com.niyaj.addonitem.AddonitemNavGraph +import com.niyaj.address.AddressNavGraph +import com.niyaj.cart.CartNavGraph +import com.niyaj.cart_selected.CartselectedNavGraph +import com.niyaj.cartorder.CartorderNavGraph +import com.niyaj.category.CategoryNavGraph +import com.niyaj.charges.ChargesNavGraph +import com.niyaj.customer.CustomerNavGraph +import com.niyaj.employee.EmployeeNavGraph +import com.niyaj.employee_absent.EmployeeabsentNavGraph +import com.niyaj.employee_payment.EmployeepaymentNavGraph +import com.niyaj.expenses.ExpensesNavGraph +import com.niyaj.home.HomeNavGraph +import com.niyaj.order.OrderNavGraph +import com.niyaj.printer_info.PrinterinfoNavGraph +import com.niyaj.product.ProductNavGraph +import com.niyaj.profile.ProfileNavGraph +import com.ramcosta.composedestinations.spec.DestinationSpec +import com.ramcosta.composedestinations.spec.NavGraphSpec + +@ExperimentalComposeUiApi +object RootNavGraph : NavGraphSpec { + + override val route = "root" + + override val destinationsByRoute = emptyMap>() + + override val startRoute = HomeNavGraph + + override val nestedNavGraphs = listOf( + AddonitemNavGraph, + AddressNavGraph, + CartNavGraph, + CartselectedNavGraph, + CartorderNavGraph, + CategoryNavGraph, + ChargesNavGraph, + CustomerNavGraph, + EmployeeNavGraph, + EmployeeabsentNavGraph, + EmployeepaymentNavGraph, + ExpensesNavGraph, + HomeNavGraph, + OrderNavGraph, + ProductNavGraph, + ProfileNavGraph, + PrinterinfoNavGraph, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/ui/PoposApp.kt b/app/src/main/java/com/niyaj/poposroom/ui/PoposApp.kt new file mode 100644 index 00000000..eac825f6 --- /dev/null +++ b/app/src/main/java/com/niyaj/poposroom/ui/PoposApp.kt @@ -0,0 +1,53 @@ +package com.niyaj.poposroom.ui + +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.niyaj.data.utils.NetworkMonitor +import com.niyaj.designsystem.components.PoposBackground +import com.niyaj.designsystem.components.PoposGradientBackground +import com.niyaj.designsystem.theme.GradientColors +import com.niyaj.poposroom.navigation.PoposNavHost +import com.niyaj.poposroom.navigation.RootNavGraph + +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun PoposApp( + windowSizeClass: WindowSizeClass, + networkMonitor: NetworkMonitor, + appState: PoposAppState = rememberPoposAppState( + networkMonitor = networkMonitor, + windowSizeClass = windowSizeClass, + ), +) { + PoposBackground { + PoposGradientBackground( + gradientColors = GradientColors(), + ) { + val snackbarHostState = remember { SnackbarHostState() } + + val isOffline by appState.isOffline.collectAsStateWithLifecycle() + + // If user is not connected to the internet show a snack bar to inform them. + LaunchedEffect(isOffline) { + if (isOffline) { + snackbarHostState.showSnackbar( + message = "You are not connected to the internet", + duration = SnackbarDuration.Indefinite, + ) + } + } + + PoposNavHost( + appState = appState, + startRoute = RootNavGraph.startRoute, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/ui/PoposAppState.kt b/app/src/main/java/com/niyaj/poposroom/ui/PoposAppState.kt new file mode 100644 index 00000000..2548ab3f --- /dev/null +++ b/app/src/main/java/com/niyaj/poposroom/ui/PoposAppState.kt @@ -0,0 +1,88 @@ +package com.niyaj.poposroom.ui + +import androidx.compose.material3.windowsizeclass.WindowSizeClass +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.navigation.NavController +import androidx.navigation.NavDestination +import androidx.navigation.NavHostController +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import com.niyaj.data.utils.NetworkMonitor +import com.niyaj.ui.utils.TrackDisposableJank +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +@Composable +fun rememberPoposAppState( + windowSizeClass: WindowSizeClass, + networkMonitor: NetworkMonitor, +// userNewsResourceRepository: UserNewsResourceRepository, + coroutineScope: CoroutineScope = rememberCoroutineScope(), + navController: NavHostController = rememberNavController(), +): PoposAppState { + NavigationTrackingSideEffect(navController) + return remember( + navController, + coroutineScope, + windowSizeClass, + networkMonitor, +// userNewsResourceRepository, + ) { + PoposAppState( + navController, + coroutineScope, + windowSizeClass, + networkMonitor, +// userNewsResourceRepository, + ) + } +} + +@Stable +class PoposAppState( + val navController: NavHostController, + coroutineScope: CoroutineScope, + val windowSizeClass: WindowSizeClass, + networkMonitor: NetworkMonitor, +// userNewsResourceRepository: UserNewsResourceRepository, +) { + val currentDestination: NavDestination? + @Composable get() = navController + .currentBackStackEntryAsState().value?.destination + + val isOffline = networkMonitor.isOnline + .map(Boolean::not) + .stateIn( + scope = coroutineScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = false, + ) +} + +/** + * Stores information about navigation events to be used with JankStats + */ +@Composable +private fun NavigationTrackingSideEffect(navController: NavHostController) { + TrackDisposableJank(navController) { metricsHolder -> + val listener = NavController.OnDestinationChangedListener { _, destination, _ -> + metricsHolder.state?.putState("Navigation", destination.route.toString()) + } + + navController.addOnDestinationChangedListener(listener) + + onDispose { + navController.removeOnDestinationChangedListener(listener) + } + } +} + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9c..00000000 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 0f4d5403..00000000 --- a/app/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - diff --git a/app/src/main/res/drawable/logo.webp b/app/src/main/res/drawable/logo.webp deleted file mode 100644 index b712a5c9..00000000 Binary files a/app/src/main/res/drawable/logo.webp and /dev/null differ diff --git a/app/src/main/res/drawable/popos.xml b/app/src/main/res/drawable/popos.xml deleted file mode 100644 index 59c33343..00000000 --- a/app/src/main/res/drawable/popos.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - diff --git a/app/src/main/res/drawable/popos_splash.xml b/app/src/main/res/drawable/popos_splash.xml new file mode 100644 index 00000000..59517582 --- /dev/null +++ b/app/src/main/res/drawable/popos_splash.xml @@ -0,0 +1,532 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 7353dbd1..036d09bc 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 7353dbd1..036d09bc 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp index 57ff91d0..652366be 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp new file mode 100644 index 00000000..5d04d142 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp index 374aa346..3e9d7982 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index b8ab2dbc..59771ae7 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp new file mode 100644 index 00000000..dc1e1a71 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp index 6aea304b..c26b03fd 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index 518fe511..3d149975 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp new file mode 100644 index 00000000..ee68b090 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index 22e38aeb..a0e02e93 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp index b550535e..6709872b 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp new file mode 100644 index 00000000..4773a21a Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp index 7aea00c1..1203e8fa 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index 9b653a2f..0748fd04 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp new file mode 100644 index 00000000..e2d087ee Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index ec690f1d..48274a76 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 97539d19..e86ad596 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -8,4 +8,5 @@ #FF000000 #FFFFFFFF #FFFDFDF5 + #FF7533DC \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml index 8e0b52b3..067d0be8 100644 --- a/app/src/main/res/values/ic_launcher_background.xml +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - #8EC640 + #5B2D89 \ No newline at end of file diff --git a/app/src/main/res/values/splash.xml b/app/src/main/res/values/splash.xml index 0ba0a315..c7a9bc01 100644 --- a/app/src/main/res/values/splash.xml +++ b/app/src/main/res/values/splash.xml @@ -2,7 +2,7 @@ diff --git a/build-logic/README.md b/build-logic/README.md new file mode 100644 index 00000000..0458b4fb --- /dev/null +++ b/build-logic/README.md @@ -0,0 +1,38 @@ +# Convention Plugins + +The `build-logic` folder defines project-specific convention plugins, used to keep a single +source of truth for common module configurations. + +This approach is heavily based on +[https://developer.squareup.com/blog/herding-elephants/](https://developer.squareup.com/blog/herding-elephants/) +and +[https://github.com/jjohannes/idiomatic-gradle](https://github.com/jjohannes/idiomatic-gradle). + +By setting up convention plugins in `build-logic`, we can avoid duplicated build script setup, +messy `subproject` configurations, without the pitfalls of the `buildSrc` directory. + +`build-logic` is an included build, as configured in the root +[`settings.gradle.kts`](../settings.gradle.kts). + +Inside `build-logic` is a `convention` module, which defines a set of plugins that all normal +modules can use to configure themselves. + +`build-logic` also includes a set of `Kotlin` files used to share logic between plugins themselves, +which is most useful for configuring Android components (libraries vs applications) with shared +code. + +These plugins are *additive* and *composable*, and try to only accomplish a single responsibility. +Modules can then pick and choose the configurations they need. +If there is one-off logic for a module without shared code, it's preferable to define that directly +in the module's `build.gradle`, as opposed to creating a convention plugin with module-specific +setup. + +Current list of convention plugins: + +- [`nowinandroid.android.application`](convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt), + [`nowinandroid.android.library`](convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt), + [`nowinandroid.android.test`](convention/src/main/kotlin/AndroidTestConventionPlugin.kt): + Configures common Android and Kotlin options. +- [`nowinandroid.android.application.compose`](convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt), + [`nowinandroid.android.library.compose`](convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt): + Configures Jetpack Compose options diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts new file mode 100644 index 00000000..b5d658b3 --- /dev/null +++ b/build-logic/convention/build.gradle.kts @@ -0,0 +1,96 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + `kotlin-dsl` +} + +group = "com.niyaj.samples.apps.popos.buildlogic" + +// Configure the build-logic plugins to target JDK 17 +// This matches the JDK used to build the project, and is not related to what is running on device. +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +//tasks.withType().configureEach { +// kotlinOptions { +// jvmTarget = JavaVersion.VERSION_17.toString() +// } +//} + +dependencies { + compileOnly(libs.android.gradlePlugin) + compileOnly(libs.kotlin.gradlePlugin) + compileOnly(libs.ksp.gradlePlugin) + compileOnly(libs.hilt.gradlePlugin) +} + +gradlePlugin { + plugins { + register("androidApplicationCompose") { + id = "popos.android.application.compose" + implementationClass = "AndroidApplicationComposeConventionPlugin" + } + register("androidApplication") { + id = "popos.android.application" + implementationClass = "AndroidApplicationConventionPlugin" + } + register("androidApplicationJacoco") { + id = "popos.android.application.jacoco" + implementationClass = "AndroidApplicationJacocoConventionPlugin" + } + register("androidLibraryCompose") { + id = "popos.android.library.compose" + implementationClass = "AndroidLibraryComposeConventionPlugin" + } + register("androidLibrary") { + id = "popos.android.library" + implementationClass = "AndroidLibraryConventionPlugin" + } + register("androidFeature") { + id = "popos.android.feature" + implementationClass = "AndroidFeatureConventionPlugin" + } + register("androidTest") { + id = "popos.android.test" + implementationClass = "AndroidTestConventionPlugin" + } + register("androidHilt") { + id = "popos.android.hilt" + implementationClass = "AndroidHiltConventionPlugin" + } + register("androidLibraryJacoco") { + id = "popos.android.library.jacoco" + implementationClass = "AndroidLibraryJacocoConventionPlugin" + } + register("androidFlavors") { + id = "popos.android.application.flavors" + implementationClass = "AndroidApplicationFlavorsConventionPlugin" + } + register("androidRoom") { + id = "popos.android.room" + implementationClass = "AndroidRoomConventionPlugin" + } + register("jvmLibrary") { + id = "popos.jvm.library" + implementationClass = "JvmLibraryConventionPlugin" + } + } +} diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt new file mode 100644 index 00000000..1dee6ed2 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationComposeConventionPlugin.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.dsl.ApplicationExtension +import com.niyaj.samples.apps.popos.configureAndroidCompose +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType + +class AndroidApplicationComposeConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply("com.android.application") + val extension = extensions.getByType() + configureAndroidCompose(extension) + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt new file mode 100644 index 00000000..ec06108f --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.dsl.ApplicationExtension +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.niyaj.samples.apps.popos.configureGradleManagedDevices +import com.niyaj.samples.apps.popos.configureKotlinAndroid +import com.niyaj.samples.apps.popos.configurePrintApksTask +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class AndroidApplicationConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.application") + apply("org.jetbrains.kotlin.android") + } + + extensions.configure { + configureKotlinAndroid(this) + defaultConfig.targetSdk = 34 + configureGradleManagedDevices(this) + } + extensions.configure { + configurePrintApksTask(this) + } + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt new file mode 100644 index 00000000..eb86c361 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationFlavorsConventionPlugin.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.dsl.ApplicationExtension +import com.niyaj.samples.apps.popos.configureFlavors +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class AndroidApplicationFlavorsConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + extensions.configure { + configureFlavors(this) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt new file mode 100644 index 00000000..f27c805c --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidApplicationJacocoConventionPlugin.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.niyaj.samples.apps.popos.configureJacoco +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType + +class AndroidApplicationJacocoConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("org.gradle.jacoco") + apply("com.android.application") + } + val extension = extensions.getByType() + configureJacoco(extension) + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt new file mode 100644 index 00000000..d8af8cd4 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidFeatureConventionPlugin.kt @@ -0,0 +1,45 @@ +import com.android.build.gradle.LibraryExtension +import com.niyaj.samples.apps.popos.configureGradleManagedDevices +import com.niyaj.samples.apps.popos.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.kotlin + +class AndroidFeatureConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply { + apply("popos.android.library") + apply("popos.android.hilt") + } + extensions.configure { + defaultConfig { + testInstrumentationRunner = "com.niyaj.testing.PoposTestRunner" + } + configureGradleManagedDevices(this) + } + + dependencies { + add("implementation", project(":core:model")) + add("implementation", project(":core:ui")) + add("implementation", project(":core:designsystem")) + add("implementation", project(":core:data")) + add("implementation", project(":core:common")) + add("implementation", project(":core:domain")) + + add("testImplementation", kotlin("test")) + add("testImplementation", project(":core:testing")) + add("androidTestImplementation", kotlin("test")) + add("androidTestImplementation", project(":core:testing")) + + add("implementation", libs.findLibrary("androidx.hilt.navigation.compose").get()) + add("implementation", libs.findLibrary("androidx.lifecycle.runtimeCompose").get()) + add("implementation", libs.findLibrary("androidx.lifecycle.viewModelCompose").get()) + + add("implementation", libs.findLibrary("kotlinx.coroutines.android").get()) + } + } + } +} diff --git a/build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt new file mode 100644 index 00000000..df135041 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidHiltConventionPlugin.kt @@ -0,0 +1,28 @@ + +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.getByType + +class AndroidHiltConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("dagger.hilt.android.plugin") + // KAPT must go last to avoid build warnings. + // See: https://stackoverflow.com/questions/70550883/warning-the-following-options-were-not-recognized-by-any-processor-dagger-f + apply("org.jetbrains.kotlin.kapt") + } + + val libs = extensions.getByType().named("libs") + dependencies { + "implementation"(libs.findLibrary("hilt.android").get()) + "kapt"(libs.findLibrary("hilt.compiler").get()) + "kaptAndroidTest"(libs.findLibrary("hilt.compiler").get()) + } + + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt new file mode 100644 index 00000000..d0ca740b --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryComposeConventionPlugin.kt @@ -0,0 +1,16 @@ + +import com.android.build.gradle.LibraryExtension +import com.niyaj.samples.apps.popos.configureAndroidCompose +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType + +class AndroidLibraryComposeConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply("com.android.library") + val extension = extensions.getByType() + configureAndroidCompose(extension) + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt new file mode 100644 index 00000000..8195e0d0 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryConventionPlugin.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.variant.LibraryAndroidComponentsExtension +import com.android.build.gradle.LibraryExtension +import com.niyaj.samples.apps.popos.configureFlavors +import com.niyaj.samples.apps.popos.configureGradleManagedDevices +import com.niyaj.samples.apps.popos.configureKotlinAndroid +import com.niyaj.samples.apps.popos.configurePrintApksTask +import com.niyaj.samples.apps.popos.disableUnnecessaryAndroidTests +import com.niyaj.samples.apps.popos.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.kotlin + +class AndroidLibraryConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.library") + apply("org.jetbrains.kotlin.android") + } + + extensions.configure { + configureKotlinAndroid(this) + defaultConfig.targetSdk = 34 + configureFlavors(this) + configureGradleManagedDevices(this) + } + extensions.configure { + configurePrintApksTask(this) + disableUnnecessaryAndroidTests(target) + } + configurations.configureEach { + resolutionStrategy { + force(libs.findLibrary("junit4").get()) + // Temporary workaround for https://issuetracker.google.com/174733673 + force("org.objenesis:objenesis:2.6") + } + } + dependencies { + add("androidTestImplementation", kotlin("test")) + add("testImplementation", kotlin("test")) + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt new file mode 100644 index 00000000..d007b089 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidLibraryJacocoConventionPlugin.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.variant.LibraryAndroidComponentsExtension +import com.niyaj.samples.apps.popos.configureJacoco +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType + +class AndroidLibraryJacocoConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("org.gradle.jacoco") + apply("com.android.library") + } + val extension = extensions.getByType() + configureJacoco(extension) + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt new file mode 100644 index 00000000..ca12df23 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidRoomConventionPlugin.kt @@ -0,0 +1,50 @@ + +import com.google.devtools.ksp.gradle.KspExtension +import com.niyaj.samples.apps.popos.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.getByType +import org.gradle.process.CommandLineArgumentProvider +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.distsDirectory +import java.io.File + +class AndroidRoomConventionPlugin : Plugin { + + override fun apply(target: Project) { + with(target) { + pluginManager.apply("com.google.devtools.ksp") + + extensions.configure { + // The schemas directory contains a schema file for each version of the Room database. + // This is required to enable Room auto migrations. + // See https://developer.android.com/reference/kotlin/androidx/room/AutoMigration. + arg(RoomSchemaArgProvider(File(projectDir, "schemas"))) + } + + dependencies { + + add("implementation", libs.findLibrary("room.runtime").get()) + add("implementation", libs.findLibrary("room.ktx").get()) + add("ksp", libs.findLibrary("room.compiler").get()) + } + } + } + + /** + * https://issuetracker.google.com/issues/132245929 + * [Export schemas](https://developer.android.com/training/data-storage/room/migrating-db-versions#export-schemas) + */ + class RoomSchemaArgProvider( + @get:InputDirectory + @get:PathSensitive(PathSensitivity.RELATIVE) + val schemaDir: File, + ) : CommandLineArgumentProvider { + override fun asArguments() = listOf("room.schemaLocation=${schemaDir.path}") + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt new file mode 100644 index 00000000..ea178ad7 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/AndroidTestConventionPlugin.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.gradle.TestExtension +import com.niyaj.samples.apps.popos.configureGradleManagedDevices +import com.niyaj.samples.apps.popos.configureKotlinAndroid +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure + +class AndroidTestConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.android.test") + apply("org.jetbrains.kotlin.android") + } + + extensions.configure { + configureKotlinAndroid(this) + defaultConfig.targetSdk = 33 + configureGradleManagedDevices(this) + } + } + } + +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt new file mode 100644 index 00000000..5a95fac4 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/JvmLibraryConventionPlugin.kt @@ -0,0 +1,14 @@ +import com.niyaj.samples.apps.popos.configureKotlinJvm +import org.gradle.api.Plugin +import org.gradle.api.Project + +class JvmLibraryConventionPlugin : Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("org.jetbrains.kotlin.jvm") + } + configureKotlinJvm() + } + } +} diff --git a/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/AndroidCompose.kt b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/AndroidCompose.kt new file mode 100644 index 00000000..850274f9 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/AndroidCompose.kt @@ -0,0 +1,58 @@ + +package com.niyaj.samples.apps.popos + +import com.android.build.api.dsl.CommonExtension +import org.gradle.api.Project +import org.gradle.kotlin.dsl.dependencies +import java.io.File + +/** + * Configure Compose-specific options + */ +internal fun Project.configureAndroidCompose( + commonExtension: CommonExtension<*, *, *, *>, +) { + commonExtension.apply { + buildFeatures { + compose = true + } + + composeOptions { + kotlinCompilerExtensionVersion = libs.findVersion("androidxComposeCompiler").get().toString() + } + + kotlinOptions { + freeCompilerArgs = freeCompilerArgs + buildComposeMetricsParameters() + } + + dependencies { + val bom = libs.findLibrary("androidx-compose-bom").get() + add("implementation", platform(bom)) + add("androidTestImplementation", platform(bom)) + } + } +} + +private fun Project.buildComposeMetricsParameters(): List { + val metricParameters = mutableListOf() + val enableMetricsProvider = project.providers.gradleProperty("enableComposeCompilerMetrics") + val enableMetrics = (enableMetricsProvider.orNull == "true") + if (enableMetrics) { + val metricsFolder = File(project.buildDir, "compose-metrics") + metricParameters.add("-P") + metricParameters.add( + "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + metricsFolder.absolutePath + ) + } + + val enableReportsProvider = project.providers.gradleProperty("enableComposeCompilerReports") + val enableReports = (enableReportsProvider.orNull == "true") + if (enableReports) { + val reportsFolder = File(project.buildDir, "compose-reports") + metricParameters.add("-P") + metricParameters.add( + "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + reportsFolder.absolutePath + ) + } + return metricParameters.toList() +} diff --git a/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/AndroidInstrumentedTests.kt b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/AndroidInstrumentedTests.kt new file mode 100644 index 00000000..dbb1b896 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/AndroidInstrumentedTests.kt @@ -0,0 +1,19 @@ +package com.niyaj.samples.apps.popos + +import com.android.build.api.variant.LibraryAndroidComponentsExtension +import org.gradle.api.Project + +/** + * Disable unnecessary Android instrumented tests for the [project] if there is no `androidTest` folder. + * Otherwise, these projects would be compiled, packaged, installed and ran only to end-up with the following message: + * + * > Starting 0 tests on AVD + * + * Note: this could be improved by checking other potential sourceSets based on buildTypes and flavors. + */ +internal fun LibraryAndroidComponentsExtension.disableUnnecessaryAndroidTests( + project: Project, +) = beforeVariants { + it.enableAndroidTest = it.enableAndroidTest + && project.projectDir.resolve("src/androidTest").exists() +} diff --git a/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/GradleManagedDevices.kt b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/GradleManagedDevices.kt new file mode 100644 index 00000000..7bd7c979 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/GradleManagedDevices.kt @@ -0,0 +1,47 @@ + +package com.niyaj.samples.apps.popos + +import com.android.build.api.dsl.CommonExtension +import com.android.build.api.dsl.ManagedVirtualDevice +import org.gradle.kotlin.dsl.invoke +import java.util.Locale + +/** + * Configure project for Gradle managed devices + */ +internal fun configureGradleManagedDevices( + commonExtension: CommonExtension<*, *, *, *>, +) { + val deviceConfigs = listOf( + DeviceConfig("Pixel 4", 30, "aosp-atd"), + DeviceConfig("Pixel 6", 31, "aosp"), + DeviceConfig("Pixel C", 30, "aosp-atd"), + ) + + commonExtension.testOptions { + managedDevices { + devices { + deviceConfigs.forEach { deviceConfig -> + maybeCreate(deviceConfig.taskName, ManagedVirtualDevice::class.java).apply { + device = deviceConfig.device + apiLevel = deviceConfig.apiLevel + systemImageSource = deviceConfig.systemImageSource + } + } + } + } + } +} + +private data class DeviceConfig( + val device: String, + val apiLevel: Int, + val systemImageSource: String, +) { + val taskName = buildString { + append(device.lowercase(Locale.ROOT).replace(" ", "")) + append("api") + append(apiLevel.toString()) + append(systemImageSource.replace("-", "")) + } +} diff --git a/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/Jacoco.kt b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/Jacoco.kt new file mode 100644 index 00000000..d5c469d2 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/Jacoco.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.niyaj.samples.apps.popos + +import com.android.build.api.variant.AndroidComponentsExtension +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.register +import org.gradle.kotlin.dsl.withType +import org.gradle.testing.jacoco.plugins.JacocoPluginExtension +import org.gradle.testing.jacoco.plugins.JacocoTaskExtension +import org.gradle.testing.jacoco.tasks.JacocoReport +import java.util.Locale + +private val coverageExclusions = listOf( + // Android + "**/R.class", + "**/R\$*.class", + "**/BuildConfig.*", + "**/Manifest*.*" +) + +internal fun Project.configureJacoco( + androidComponentsExtension: AndroidComponentsExtension<*, *, *>, +) { + val libs = extensions.getByType().named("libs") + + configure { + toolVersion = libs.findVersion("jacoco").get().toString() + } + + val jacocoTestReport = tasks.create("jacocoTestReport") + + androidComponentsExtension.onVariants { variant -> + val testTaskName = "test${variant.name.replaceFirstChar { + if (it.isLowerCase()) it.titlecase( + Locale.ROOT + ) else it.toString() + }}UnitTest" + + val reportTask = tasks.register("jacoco${testTaskName.replaceFirstChar { + if (it.isLowerCase()) it.titlecase( + Locale.ROOT + ) else it.toString() + }}Report", JacocoReport::class) { + dependsOn(testTaskName) + + reports { + xml.required.set(true) + html.required.set(true) + } + + classDirectories.setFrom( + fileTree("$buildDir/tmp/kotlin-classes/${variant.name}") { + exclude(coverageExclusions) + } + ) + + sourceDirectories.setFrom(files("$projectDir/src/main/java", "$projectDir/src/main/kotlin")) + executionData.setFrom(file("$buildDir/jacoco/$testTaskName.exec")) + } + + jacocoTestReport.dependsOn(reportTask) + } + + tasks.withType().configureEach { + configure { + // Required for JaCoCo + Robolectric + // https://github.com/robolectric/robolectric/issues/2230 + // TODO: Consider removing if not we don't add Robolectric + isIncludeNoLocationClasses = true + + // Required for JDK 11 with the above + // https://github.com/gradle/gradle/issues/5184#issuecomment-391982009 + excludes = listOf("jdk.internal.*") + } + } +} diff --git a/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/KotlinAndroid.kt b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/KotlinAndroid.kt new file mode 100644 index 00000000..f450fc59 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/KotlinAndroid.kt @@ -0,0 +1,85 @@ + +package com.niyaj.samples.apps.popos + +import com.android.build.api.dsl.CommonExtension +import org.gradle.api.JavaVersion +import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.provideDelegate +import org.gradle.kotlin.dsl.withType +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +/** + * Configure base Kotlin with Android options + */ +internal fun Project.configureKotlinAndroid( + commonExtension: CommonExtension<*, *, *, *>, +) { + commonExtension.apply { + compileSdk = 34 + + defaultConfig { + minSdk = 26 + } + + compileOptions { + // Up to Java 11 APIs are available through desugaring + // https://developer.android.com/studio/write/java11-minimal-support-table + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + isCoreLibraryDesugaringEnabled = true + } + } + + configureKotlin() + + dependencies { + add("coreLibraryDesugaring", libs.findLibrary("android.desugarJdkLibs").get()) + } +} + +fun CommonExtension<*, *, *, *>.kotlinOptions(block: KotlinJvmOptions.() -> Unit) { + (this as ExtensionAware).extensions.configure("kotlinOptions", block) +} + + +/** + * Configure base Kotlin options for JVM (non-Android) + */ +internal fun Project.configureKotlinJvm() { + extensions.configure { + // Up to Java 11 APIs are available through desugaring + // https://developer.android.com/studio/write/java11-minimal-support-table + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + configureKotlin() +} + +/** + * Configure base Kotlin options + */ +private fun Project.configureKotlin() { + // Use withType to workaround https://youtrack.jetbrains.com/issue/KT-55947 + tasks.withType().configureEach { + kotlinOptions { + // Set JVM target to 11 + jvmTarget = JavaVersion.VERSION_11.toString() + // Treat all Kotlin warnings as errors (disabled by default) + // Override by setting warningsAsErrors=true in your ~/.gradle/gradle.properties + val warningsAsErrors: String? by project + allWarningsAsErrors = warningsAsErrors.toBoolean() + freeCompilerArgs = freeCompilerArgs + listOf( + "-opt-in=kotlin.RequiresOptIn", + // Enable experimental coroutines APIs, including Flow + "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", + "-opt-in=kotlinx.coroutines.FlowPreview", + ) + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/PoposBuildType.kt b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/PoposBuildType.kt new file mode 100644 index 00000000..3a1c40e6 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/PoposBuildType.kt @@ -0,0 +1,12 @@ + +package com.niyaj.samples.apps.popos + +/** + * This is shared between :app and :benchmarks module to provide configurations type safety. + */ +@Suppress("unused") +enum class PoposBuildType(val applicationIdSuffix: String? = null) { + DEBUG(".debug"), + RELEASE, + BENCHMARK(".benchmark") +} diff --git a/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/PoposFlavor.kt b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/PoposFlavor.kt new file mode 100644 index 00000000..3f465e5c --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/PoposFlavor.kt @@ -0,0 +1,43 @@ +package com.niyaj.samples.apps.popos + +import com.android.build.api.dsl.ApplicationExtension +import com.android.build.api.dsl.ApplicationProductFlavor +import com.android.build.api.dsl.CommonExtension +import com.android.build.api.dsl.ProductFlavor +import org.gradle.api.Project + +@Suppress("EnumEntryName") +enum class FlavorDimension { + contentType +} + +// The content for the app can either come from local static data which is useful for demo +// purposes, or from a production backend server which supplies up-to-date, real content. +// These two product flavors reflect this behaviour. +@Suppress("EnumEntryName") +enum class PoposFlavor(val dimension: FlavorDimension, val applicationIdSuffix: String? = null) { + demo(FlavorDimension.contentType, applicationIdSuffix = ".demo"), + prod(FlavorDimension.contentType, ) +} + +fun Project.configureFlavors( + commonExtension: CommonExtension<*, *, *, *>, + flavorConfigurationBlock: ProductFlavor.(flavor: PoposFlavor) -> Unit = {} +) { + commonExtension.apply { + flavorDimensions += FlavorDimension.contentType.name + productFlavors { + PoposFlavor.values().forEach { + create(it.name) { + dimension = it.dimension.name + flavorConfigurationBlock(this, it) + if (this@apply is ApplicationExtension && this is ApplicationProductFlavor) { + if (it.applicationIdSuffix != null) { + this.applicationIdSuffix = it.applicationIdSuffix + } + } + } + } + } + } +} diff --git a/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/PrintTestApks.kt b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/PrintTestApks.kt new file mode 100644 index 00000000..666e4d9d --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/PrintTestApks.kt @@ -0,0 +1,83 @@ + +package com.niyaj.samples.apps.popos + +import com.android.build.api.artifact.SingleArtifact +import com.android.build.api.variant.AndroidComponentsExtension +import com.android.build.api.variant.BuiltArtifactsLoader +import com.android.build.api.variant.HasAndroidTest +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.file.Directory +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.TaskAction +import java.io.File + +internal fun Project.configurePrintApksTask(extension: AndroidComponentsExtension<*, *, *>) { + extension.onVariants { variant -> + if (variant is HasAndroidTest) { + val loader = variant.artifacts.getBuiltArtifactsLoader() + val artifact = variant.androidTest?.artifacts?.get(SingleArtifact.APK) + val javaSources = variant.androidTest?.sources?.java?.all + val kotlinSources = variant.androidTest?.sources?.kotlin?.all + + val testSources = if (javaSources != null && kotlinSources != null) { + javaSources.zip(kotlinSources) { javaDirs, kotlinDirs -> + javaDirs + kotlinDirs + } + } else javaSources ?: kotlinSources + + if (artifact != null && testSources != null) { + tasks.register( + "${variant.name}PrintTestApk", + PrintApkLocationTask::class.java + ) { + apkFolder.set(artifact) + builtArtifactsLoader.set(loader) + variantName.set(variant.name) + sources.set(testSources) + } + } + } + } +} + +internal abstract class PrintApkLocationTask : DefaultTask() { + @get:InputDirectory + abstract val apkFolder: DirectoryProperty + + @get:InputFiles + abstract val sources: ListProperty + + @get:Internal + abstract val builtArtifactsLoader: Property + + @get:Input + abstract val variantName: Property + + @TaskAction + fun taskAction() { + val hasFiles = sources.orNull?.any { directory -> + directory.asFileTree.files.any { + it.isFile && it.parentFile.path.contains("build${File.separator}generated").not() + } + } ?: throw RuntimeException("Cannot check androidTest sources") + + // Don't print APK location if there are no androidTest source files + if (!hasFiles) { + return + } + + val builtArtifacts = builtArtifactsLoader.get().load(apkFolder.get()) + ?: throw RuntimeException("Cannot load APKs") + if (builtArtifacts.elements.size != 1) + throw RuntimeException("Expected one APK !") + val apk = File(builtArtifacts.elements.single().outputFile).toPath() + println(apk) + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/ProjectExtensions.kt b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/ProjectExtensions.kt new file mode 100644 index 00000000..19dcef81 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/niyaj/samples/apps/popos/ProjectExtensions.kt @@ -0,0 +1,9 @@ +package com.niyaj.samples.apps.popos + +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalog +import org.gradle.api.artifacts.VersionCatalogsExtension +import org.gradle.kotlin.dsl.getByType + +val Project.libs + get(): VersionCatalog = extensions.getByType().named("libs") diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties new file mode 100644 index 00000000..1c9073eb --- /dev/null +++ b/build-logic/gradle.properties @@ -0,0 +1,4 @@ +# Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534 +org.gradle.parallel=true +org.gradle.caching=true +org.gradle.configureondemand=true diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 00000000..de9224e2 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,30 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +dependencyResolutionManagement { + repositories { + google() + mavenCentral() + } + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +rootProject.name = "build-logic" +include(":convention") diff --git a/build.gradle.kts b/build.gradle.kts index 87d0c8ca..2b69f999 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,14 +2,13 @@ @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed plugins { alias(libs.plugins.android.application) apply false - alias(libs.plugins.android.library) apply false - alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.jvm) apply false + alias(libs.plugins.kotlin.serialization) apply false + alias(libs.plugins.gms) apply false alias(libs.plugins.hilt) apply false - alias(libs.plugins.android.test) apply false - alias(libs.plugins.sentry) - alias(libs.plugins.androidx.baselineprofile) apply false - alias(libs.plugins.androidx.benchmark) apply false + alias(libs.plugins.ksp) apply false + alias(libs.plugins.secrets) apply false + alias(libs.plugins.kotlinAndroid) apply false } tasks.register("clean", Delete::class) { diff --git a/core/common/.gitignore b/core/common/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/common/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/common/build.gradle.kts b/core/common/build.gradle.kts new file mode 100644 index 00000000..3ac36173 --- /dev/null +++ b/core/common/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("popos.android.library") + id("popos.android.library.jacoco") + id("popos.android.hilt") +} + +android { + namespace = "com.niyaj.core.common" +} + +dependencies { + implementation(libs.kotlinx.coroutines.android) + testImplementation(project(":core:testing")) +} \ No newline at end of file diff --git a/core/common/src/androidTest/java/com/niyaj/common/ExampleInstrumentedTest.kt b/core/common/src/androidTest/java/com/niyaj/common/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..8cb442bb --- /dev/null +++ b/core/common/src/androidTest/java/com/niyaj/common/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.common + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.common.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/common/src/main/AndroidManifest.xml b/core/common/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/common/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/common/src/main/java/com/niyaj/common/decoder/Decoder.kt b/core/common/src/main/java/com/niyaj/common/decoder/Decoder.kt new file mode 100644 index 00000000..31645ecf --- /dev/null +++ b/core/common/src/main/java/com/niyaj/common/decoder/Decoder.kt @@ -0,0 +1,7 @@ +package com.niyaj.common.decoder + +interface Decoder { + fun decodeString(encodedString: String): String + + fun decodeInt(encodedInt: Int): Int +} diff --git a/core/common/src/main/java/com/niyaj/common/decoder/UriDecoder.kt b/core/common/src/main/java/com/niyaj/common/decoder/UriDecoder.kt new file mode 100644 index 00000000..7d17f6c9 --- /dev/null +++ b/core/common/src/main/java/com/niyaj/common/decoder/UriDecoder.kt @@ -0,0 +1,10 @@ +package com.niyaj.common.decoder + +import android.net.Uri +import javax.inject.Inject + +class UriDecoder @Inject constructor() : Decoder { + override fun decodeString(encodedString: String): String = Uri.decode(encodedString) + + override fun decodeInt(encodedInt: Int): Int = Uri.decode(encodedInt.toString()).toInt() +} diff --git a/core/common/src/main/java/com/niyaj/common/decoder/di/DecoderModule.kt b/core/common/src/main/java/com/niyaj/common/decoder/di/DecoderModule.kt new file mode 100644 index 00000000..0bb7cc2d --- /dev/null +++ b/core/common/src/main/java/com/niyaj/common/decoder/di/DecoderModule.kt @@ -0,0 +1,15 @@ +package com.niyaj.common.decoder.di + +import com.niyaj.common.decoder.Decoder +import com.niyaj.common.decoder.UriDecoder +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +abstract class DecoderModule { + @Binds + abstract fun bindStringDecoder(uriDecoder: UriDecoder): Decoder +} diff --git a/core/common/src/main/java/com/niyaj/common/network/PoposDispatchers.kt b/core/common/src/main/java/com/niyaj/common/network/PoposDispatchers.kt new file mode 100644 index 00000000..8e5a7307 --- /dev/null +++ b/core/common/src/main/java/com/niyaj/common/network/PoposDispatchers.kt @@ -0,0 +1,13 @@ +package com.niyaj.common.network + +import javax.inject.Qualifier +import kotlin.annotation.AnnotationRetention.RUNTIME + +@Qualifier +@Retention(RUNTIME) +annotation class Dispatcher(val dispatcher: PoposDispatchers) + +enum class PoposDispatchers { + Default, + IO, +} diff --git a/core/common/src/main/java/com/niyaj/common/network/di/CoroutineScopesModule.kt b/core/common/src/main/java/com/niyaj/common/network/di/CoroutineScopesModule.kt new file mode 100644 index 00000000..78020d33 --- /dev/null +++ b/core/common/src/main/java/com/niyaj/common/network/di/CoroutineScopesModule.kt @@ -0,0 +1,29 @@ +package com.niyaj.common.network.di + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import javax.inject.Qualifier +import javax.inject.Singleton + +@Retention(AnnotationRetention.RUNTIME) +@Qualifier +annotation class ApplicationScope + +@Module +@InstallIn(SingletonComponent::class) +object CoroutineScopesModule { + + @Provides + @Singleton + @ApplicationScope + fun providesCoroutineScope( + @Dispatcher(PoposDispatchers.Default) dispatcher: CoroutineDispatcher, + ): CoroutineScope = CoroutineScope(SupervisorJob() + dispatcher) +} diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/di/DispatchersModule.kt b/core/common/src/main/java/com/niyaj/common/network/di/DispatchersModule.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/common/di/DispatchersModule.kt rename to core/common/src/main/java/com/niyaj/common/network/di/DispatchersModule.kt index 3d7e4e87..8e3ddaef 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/di/DispatchersModule.kt +++ b/core/common/src/main/java/com/niyaj/common/network/di/DispatchersModule.kt @@ -1,7 +1,7 @@ -package com.niyaj.poposroom.features.common.di +package com.niyaj.common.network.di -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -19,4 +19,4 @@ object DispatchersModule { @Provides @Dispatcher(PoposDispatchers.Default) fun providesDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default -} \ No newline at end of file +} diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/utils/Resource.kt b/core/common/src/main/java/com/niyaj/common/result/Resource.kt similarity index 79% rename from app/src/main/java/com/niyaj/poposroom/features/common/utils/Resource.kt rename to core/common/src/main/java/com/niyaj/common/result/Resource.kt index d62d0a7c..fc13b206 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/utils/Resource.kt +++ b/core/common/src/main/java/com/niyaj/common/result/Resource.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.common.utils +package com.niyaj.common.result sealed class Resource(val data: T? = null, val message: String? = null) { class Success(data: T?): Resource(data) class Error(message: String?): Resource(message = message) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/utils/ValidationResult.kt b/core/common/src/main/java/com/niyaj/common/result/ValidationResult.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/common/utils/ValidationResult.kt rename to core/common/src/main/java/com/niyaj/common/result/ValidationResult.kt index dfe28816..f119b5a8 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/utils/ValidationResult.kt +++ b/core/common/src/main/java/com/niyaj/common/result/ValidationResult.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.utils +package com.niyaj.common.result data class ValidationResult( val successful: Boolean, diff --git a/core/common/src/main/java/com/niyaj/common/utils/Constants.kt b/core/common/src/main/java/com/niyaj/common/utils/Constants.kt new file mode 100644 index 00000000..5e4d8e5a --- /dev/null +++ b/core/common/src/main/java/com/niyaj/common/utils/Constants.kt @@ -0,0 +1,130 @@ +package com.niyaj.common.utils + +object Constants { + + const val UPDATE_MANAGER_REQUEST_CODE = 123 + const val NOTIFICATION_PERMISSION_REQUEST_CODE = 888 + const val NETWORK_PERMISSION_REQUEST_CODE = 777 + + // github login token - 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 + + const val SPLASH_SCREEN_DURATION = 100 + + const val PRINTER_ID = "PRINTER11" + + const val PRINTER_DPI = 176 + + const val PRINTER_WIDTH_MM = 58f + + const val PRINTER_NBR_LINE = 31 + + const val PAYMENT_QR_DATA = "upi://pay?pa=paytmqr281005050101zry6uqipmngr@paytm&pn=Paytm%20Merchant&paytmqr=281005050101ZRY6UQIPMNGR" + + const val PRODUCT_NAME_LENGTH = 18 + + const val PRINT_PRODUCT_WISE_REPORT_LIMIT = 30 + + const val PRINT_ADDRESS_WISE_REPORT_LIMIT = 15 + + const val PRINT_CUSTOMER_WISE_REPORT_LIMIT = 15 + + const val JSON_FILE_TYPE = "application/json" + + const val JSON_FILE_EXTENSION = ".json" + + const val BACKUP_REALM_NAME = "backup.realm" + + const val SAVEABLE_FILE_NAME = "popos" + + const val PAID = "Paid" + + const val NOT_PAID = "Not Paid" + + const val DELETE_DATA_NOTIFICATION_CHANNEL_ID = "delete_data" + const val DELETE_DATA_NOTIFICATION_CHANNEL_NAME = "Data Deletion" + const val DELETE_DATA_INTERVAL_HOUR: Long = 15 + + const val GENERATE_REPORT_CHANNEL_ID = "generate_report" + const val GENERATE_REPORT_CHANNEL_NAME = "Generating Report" + const val GENERATE_REPORT_INTERVAL_HOUR: Long = 1 + + const val SELECTED_CART_ORDER_ID = "33333333" + const val SETTINGS_ID = "11111111" + const val RESTAURANT_ID = 2222 + + const val RESTAURANT_NAME = "Popos Highlight" + const val RESTAURANT_TAGLINE = "- Pure And Tasty -" + const val RESTAURANT_DESCRIPTION = "Multi Cuisine Veg & Non-Veg Restaurant" + const val RESTAURANT_EMAIL = "poposhighlight@gmail.com" + const val RESTAURANT_SECONDARY_PHONE: String = "9597185001" + const val RESTAURANT_PRIMARY_PHONE: String = "9500825077" + const val RESTAURANT_ADDRESS = "Chinna Seeragapadi, Salem, TamilNadu, India 636308, Opp. of VIMS Hospital" + + const val RESTAURANT_LOGO_NAME = "reslogo.png" + const val RESTAURANT_PRINT_LOGO_NAME = "printlogo.png" + +// const val RESTAURANT_LOGO = R.drawable.logo_new.toString() +// const val PRINT_LOGO = R.drawable.reslogo.toString() + + //Dialog positive button test tag + const val POSITIVE_BUTTON = "positive" + const val NEGATIVE_BUTTON = "negative" + + const val STANDARD_BOTTOM_SHEET = "BottomSheet" + const val STANDARD_BOTTOM_SHEET_CLOSE_BTN = "Standard Bottom Close Button" + + const val SORT_ASCENDING = "Sort Ascending" + const val SORT_DESCENDING = "Sort Descending" + + const val ABSENT_REMINDER_NOTE = "Selected employees will be mark as absent." + const val DAILY_SALARY_REMINDER_NOTE = "Selected employees will be mark as paid." + + const val ABSENT_REMINDER_ID = "EA889977" + const val ABSENT_REMINDER_NAME = "Employee Attendance" + const val ABSENT_REMINDER_TITLE = "Did You Marked Employee Attendance?" + const val ABSENT_REMINDER_TEXT = "Don't forget to mark absent employees." + const val ABSENT_REMINDER_REQ_CODE = 9977 + + const val DAILY_SALARY_REMINDER_ID = "DAL907856" + const val DAILY_SALARY_REMINDER_NAME = "Daily Salary Reminder" + const val DAILY_SALARY_REMINDER_TITLE = "Did You Paid Employee Salary?" + const val DAILY_SALARY_REMINDER_TEXT = "Don't forget to add salary entries." + const val DAILY_SALARY_REQ_CODE = 9078 + + private const val HOST = "http://skniyajali.me/" + private const val HOST_SECURE = "https://skniyajali.me/" + + const val SALARY_HOST = "${HOST}reminder/reminder_id=$DAILY_SALARY_REMINDER_ID" + const val SALARY_HOST_SECURE = "${HOST_SECURE}reminder/reminder_id=$DAILY_SALARY_REMINDER_ID" + + const val ABSENT_HOST = "${HOST}reminder/reminder_id=$ABSENT_REMINDER_ID" + const val ABSENT_HOST_SECURE = "${HOST_SECURE}reminder/reminder_id=$ABSENT_REMINDER_ID" + + const val TEXT_FIELD_LEADING_ICON = "TextFieldLeadingIcon" + const val TEXT_FIELD_TRAILING_ICON = "TextFieldLeadingIcon" + + const val SEARCH_ITEM_NOT_FOUND = "Searched Item Not Found." + const val SEARCH_ITEM_PLACEHOLDER = "Search for items..." + + const val STANDARD_BACK_BUTTON = "StandardBackButton" + const val SEARCH_BAR_CLEAR_BUTTON = "SearchBarClearButton" + const val STANDARD_SEARCH_BAR = "StandardSearchBar" + const val FAB_TEXT = "Create New" + const val LOADING_INDICATION = "loadingIndicator" + const val SEARCH_ICON = "SearchIcon" + const val SEARCH_PLACEHOLDER = "SearchPlaceHolder" + const val SETTINGS_ICON = "SettingsIcon" + const val EDIT_ICON = "EditIcon" + const val DELETE_ICON = "DeleteIcon" + const val CLEAR_ICON = "ClearIcon" + const val SELECTALL_ICON = "SelectAllIcon" + const val PASSWORD_HIDDEN_ICON = "Password Hidden" + const val PASSWORD_SHOWN_ICON = "Password Shown" + +} + + +enum class ImportExportType { + IMPORT, + EXPORT +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/utils/Extensions.kt b/core/common/src/main/java/com/niyaj/common/utils/Extensions.kt similarity index 86% rename from app/src/main/java/com/niyaj/poposroom/features/common/utils/Extensions.kt rename to core/common/src/main/java/com/niyaj/common/utils/Extensions.kt index 707361a5..d0fc9d3f 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/utils/Extensions.kt +++ b/core/common/src/main/java/com/niyaj/common/utils/Extensions.kt @@ -1,14 +1,6 @@ -package com.niyaj.poposroom.features.common.utils +package com.niyaj.common.utils import android.text.format.DateUtils -import androidx.compose.foundation.lazy.LazyListState -import androidx.compose.foundation.lazy.grid.LazyGridState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.derivedStateOf -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import java.text.DecimalFormat import java.text.SimpleDateFormat import java.time.LocalDate @@ -22,31 +14,6 @@ import java.util.Locale val Int.safeString: String get() = if (this == 0) "" else this.toString() - -val LazyListState.isScrolled: Boolean - get() = derivedStateOf { firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0 }.value - -@Composable -fun LazyListState.isScrollingUp(): Boolean { - var previousIndex by remember(this) { mutableIntStateOf(firstVisibleItemIndex) } - var previousScrollOffset by remember(this) { mutableIntStateOf(firstVisibleItemScrollOffset) } - return remember(this) { - derivedStateOf { - if (previousIndex != firstVisibleItemIndex) { - previousIndex > firstVisibleItemIndex - } else { - previousScrollOffset >= firstVisibleItemScrollOffset - }.also { - previousIndex = firstVisibleItemIndex - previousScrollOffset = firstVisibleItemScrollOffset - } - } - }.value -} - -val LazyGridState.isScrolled: Boolean - get() = derivedStateOf { firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0 }.value - val String.toRupee get() = DecimalFormat .getCurrencyInstance(Locale("en", "IN")) @@ -135,10 +102,15 @@ val String.toDate val String.toTime get() = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(this.toLong()).toString() - val Date.toTime get() = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(this).toString() +val String.toFormattedTime + get() = SimpleDateFormat( + "hh:mm a", + Locale.getDefault() + ).format(this.toLong()).toString() + fun String.toPrettyDate(): String { val nowTime = Calendar.getInstance() val neededTime = Calendar.getInstance() @@ -290,6 +262,21 @@ fun String.safeInt(): Int { } } +fun String.safeFloat() : Float { + return if (this.isEmpty()) { + 0f + } else { + try { + this.toFloat() + } catch (e : NumberFormatException) { + 0f + } + } +} + +val Boolean.toSafeString: String + get() = if (this) "Yes" else "No" + val startOfDayTime = LocalDate.now().toMilliSecond val endOfDayTime = calculateEndOfDayTime(startOfDayTime) @@ -373,4 +360,43 @@ val getStartDateLong: Long = startTime().timeInMillis val getEndDateLong: Long = endTime().timeInMillis val Date.toTimeSpan - get() = DateUtils.getRelativeTimeSpanString(this.time).toString() \ No newline at end of file + get() = DateUtils.getRelativeTimeSpanString(this.time).toString() + +fun createDottedString(name : String, limit: Int) : String { + if (name.length > limit) { + var wordLength = 0 + var firstWordLength = 0 + val splitName = name.split(' ') + + splitName.forEachIndexed { index, word -> + if (index != 0) { + wordLength += word.length.plus(1) + } else { + firstWordLength = word.length + } + } + + val remainingLength = limit.minus(firstWordLength) + + val whiteSpace = splitName.size - 1 + + val remLength = + wordLength.plus(whiteSpace).minus(remainingLength).div(splitName.size.minus(1)) + + var newName = "" + + splitName.forEachIndexed { index, name1 -> + if (index != 0) { + val wordLen = name1.length.minus(remLength.plus(1)) + val dottedName = + if (wordLen <= 0) name1.substring(0, 1) else name1.substring(0, wordLen) + .plus(".") + newName += " $dottedName" + } else { + newName = name1 + } + } + + return newName + } else return name +} diff --git a/core/common/src/test/java/com/niyaj/common/ExampleUnitTest.kt b/core/common/src/test/java/com/niyaj/common/ExampleUnitTest.kt new file mode 100644 index 00000000..739e0f52 --- /dev/null +++ b/core/common/src/test/java/com/niyaj/common/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.common + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/core/data/.gitignore b/core/data/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/data/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/data/build.gradle.kts b/core/data/build.gradle.kts new file mode 100644 index 00000000..dd0a9846 --- /dev/null +++ b/core/data/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.library") + id("popos.android.library.jacoco") + id("popos.android.hilt") + id("kotlinx-serialization") +} + +android { + namespace = "com.niyaj.core.data" + compileSdk = libs.versions.compileSdk.get().toInt() + + testOptions { + unitTests { + isIncludeAndroidResources = true + isReturnDefaultValues = true + } + } +} + +dependencies { + implementation(project(":core:common")) + implementation(project(":core:database")) + implementation(project(":core:model")) + implementation(libs.androidx.core.ktx) + implementation(libs.kotlinx.coroutines.android) + implementation(libs.kotlinx.serialization.json) + + testImplementation(project(":core:testing")) +} \ No newline at end of file diff --git a/core/data/src/androidTest/java/com/niyaj/data/ExampleInstrumentedTest.kt b/core/data/src/androidTest/java/com/niyaj/data/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..9a65bc37 --- /dev/null +++ b/core/data/src/androidTest/java/com/niyaj/data/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.data + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.data.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/data/src/main/AndroidManifest.xml b/core/data/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8c4c9826 --- /dev/null +++ b/core/data/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/data/repository/AbsentRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/AbsentRepositoryImpl.kt similarity index 73% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/data/repository/AbsentRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/AbsentRepositoryImpl.kt index f551ed9c..30d1573f 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/data/repository/AbsentRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/AbsentRepositoryImpl.kt @@ -1,21 +1,25 @@ -package com.niyaj.poposroom.features.employee_absent.data.repository - -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee.domain.model.filterEmployee -import com.niyaj.poposroom.features.employee_absent.data.dao.AbsentDao -import com.niyaj.poposroom.features.employee_absent.domain.model.Absent -import com.niyaj.poposroom.features.employee_absent.domain.model.EmployeeWithAbsent -import com.niyaj.poposroom.features.employee_absent.domain.model.EmployeeWithAbsentCrossRef -import com.niyaj.poposroom.features.employee_absent.domain.model.filterAbsent -import com.niyaj.poposroom.features.employee_absent.domain.repository.AbsentRepository -import com.niyaj.poposroom.features.employee_absent.domain.repository.AbsentValidationRepository -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_DATE_EMPTY -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_DATE_EXIST -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_EMPLOYEE_NAME_EMPTY +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.AbsentRepository +import com.niyaj.data.repository.validation.AbsentValidationRepository +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_DATE_EMPTY +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_DATE_EXIST +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_EMPLOYEE_NAME_EMPTY +import com.niyaj.database.dao.AbsentDao +import com.niyaj.database.model.EmployeeEntity +import com.niyaj.database.model.EmployeeWithAbsentCrossRef +import com.niyaj.database.model.EmployeeWithAbsentsDto +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Absent +import com.niyaj.model.Employee +import com.niyaj.model.EmployeeWithAbsents +import com.niyaj.model.filterAbsent +import com.niyaj.model.filterEmployee import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -26,20 +30,26 @@ import kotlinx.coroutines.withContext class AbsentRepositoryImpl( private val absentDao: AbsentDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : AbsentRepository, AbsentValidationRepository { - override fun getAllEmployee(): Flow> = absentDao.getAllEmployee() + override fun getAllEmployee(): Flow> = + absentDao.getAllEmployee().mapLatest { list -> + list.map(EmployeeEntity::asExternalModel) + } override suspend fun getEmployeeById(employeeId: Int): Employee? { return withContext(ioDispatcher) { - absentDao.getEmployeeById(employeeId) + absentDao.getEmployeeById(employeeId)?.asExternalModel() } } - override suspend fun getAllEmployeeAbsents(searchText: String): Flow> { + override suspend fun getAllEmployeeAbsents(searchText: String): Flow> { return withContext(ioDispatcher) { absentDao.getAllAbsentEmployee().mapLatest { list -> - list.filter { it.employee.filterEmployee(searchText) } + list.map(EmployeeWithAbsentsDto::asExternalModel) + .filter { + it.employee.filterEmployee(searchText) + } } } } @@ -47,7 +57,9 @@ class AbsentRepositoryImpl( override suspend fun getAllAbsent(searchText: String): Flow> { return withContext(ioDispatcher) { absentDao.getAllAbsent().mapLatest { list -> - list.filter { it.filterAbsent(searchText) } + list + .map { it.asExternalModel() } + .filter { it.filterAbsent(searchText) } } } } @@ -55,9 +67,9 @@ class AbsentRepositoryImpl( override suspend fun getAbsentById(absentId: Int): Resource { return try { withContext(ioDispatcher) { - Resource.Success(absentDao.getAbsentById(absentId)) + Resource.Success(absentDao.getAbsentById(absentId)?.asExternalModel()) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message) } } @@ -73,7 +85,7 @@ class AbsentRepositoryImpl( val hasError = listOf(validateAbsentEmployee, validateAbsentDate).any { !it.successful } if (!hasError) { - val result = absentDao.insertOrIgnoreAbsent(newAbsent) + val result = absentDao.insertOrIgnoreAbsent(newAbsent.toEntity()) if (result > 0) { absentDao.upsertEmployeeWithAbsentCrossReference( @@ -82,7 +94,7 @@ class AbsentRepositoryImpl( } Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to validate attendance") } } catch (e: Exception) { @@ -102,10 +114,10 @@ class AbsentRepositoryImpl( val hasError = listOf(validateAbsentEmployee, validateAbsentDate).any { !it.successful } if (!hasError) { - val result = absentDao.updateAbsent(newAbsent) + val result = absentDao.updateAbsent(newAbsent.toEntity()) Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to validate attendance") } } catch (e: Exception) { @@ -125,7 +137,7 @@ class AbsentRepositoryImpl( val hasError = listOf(validateAbsentEmployee, validateAbsentDate).any { !it.successful } if (!hasError) { - val result = absentDao.upsertAbsent(newAbsent) + val result = absentDao.upsertAbsent(newAbsent.toEntity()) if (result > 0) { absentDao.upsertEmployeeWithAbsentCrossReference( @@ -134,7 +146,7 @@ class AbsentRepositoryImpl( } Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to validate attendance") } } catch (e: Exception) { @@ -149,7 +161,7 @@ class AbsentRepositoryImpl( Resource.Success(result > 0) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error("Unable to delete absent entry") } } @@ -161,7 +173,7 @@ class AbsentRepositoryImpl( Resource.Success(result > 0) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error("Unable to delete absent entries") } } @@ -169,7 +181,7 @@ class AbsentRepositoryImpl( override suspend fun validateAbsentDate( absentDate: String, employeeId: Int?, - absentId: Int? + absentId: Int?, ): ValidationResult { if (absentDate.isEmpty()) { return ValidationResult( @@ -183,7 +195,7 @@ class AbsentRepositoryImpl( absentDao.findEmployeeByDate(absentDate, employeeId, absentId) != null } - if(serverResult){ + if (serverResult) { return ValidationResult( successful = false, errorMessage = ABSENT_DATE_EXIST, diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/data/repository/AddOnItemRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/AddOnItemRepositoryImpl.kt similarity index 76% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/data/repository/AddOnItemRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/AddOnItemRepositoryImpl.kt index c3c06438..83947799 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/data/repository/AddOnItemRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/AddOnItemRepositoryImpl.kt @@ -1,15 +1,17 @@ -package com.niyaj.poposroom.features.addon_item.data.repository - -import com.niyaj.poposroom.features.addon_item.data.dao.AddOnItemDao -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.addon_item.domain.model.searchAddOnItem -import com.niyaj.poposroom.features.addon_item.domain.repository.AddOnItemRepository -import com.niyaj.poposroom.features.addon_item.domain.repository.AddOnItemValidationRepository -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.AddOnItemRepository +import com.niyaj.data.repository.validation.AddOnItemValidationRepository +import com.niyaj.data.utils.AddOnTestTags +import com.niyaj.database.dao.AddOnItemDao +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.AddOnItem +import com.niyaj.model.searchAddOnItem import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -24,14 +26,17 @@ class AddOnItemRepositoryImpl( ) : AddOnItemRepository, AddOnItemValidationRepository { override suspend fun getAllAddOnItem(searchText: String): Flow> { return withContext(ioDispatcher) { - addOnItemDao.getAllAddOnItems().mapLatest { it.searchAddOnItem(searchText) } + addOnItemDao.getAllAddOnItems() + .mapLatest { list -> + list.map { it.asExternalModel() }.searchAddOnItem(searchText) + } } } override suspend fun getAddOnItemById(itemId: Int): Resource { return try { withContext(ioDispatcher) { - Resource.Success(addOnItemDao.getAddOnItemById(itemId)) + Resource.Success(addOnItemDao.getAddOnItemById(itemId)?.asExternalModel()) } } catch (e: Exception) { Resource.Error(e.message) @@ -47,10 +52,10 @@ class AddOnItemRepositoryImpl( val hasError = listOf(validateName, validatePrice).any { !it.successful } if (!hasError) { - val result = addOnItemDao.insertOrIgnoreAddOnItem(newAddOnItem) + val result = addOnItemDao.insertOrIgnoreAddOnItem(newAddOnItem.toEntity()) Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to create addon item") } } @@ -68,10 +73,10 @@ class AddOnItemRepositoryImpl( val hasError = listOf(validateName, validatePrice).any { !it.successful } if (!hasError) { - val result = addOnItemDao.updateAddOnItem(newAddOnItem) + val result = addOnItemDao.updateAddOnItem(newAddOnItem.toEntity()) Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to update addon item") } } @@ -89,11 +94,11 @@ class AddOnItemRepositoryImpl( if (!hasError) { val result = withContext(ioDispatcher) { - addOnItemDao.upsertAddOnItem(newAddOnItem) + addOnItemDao.upsertAddOnItem(newAddOnItem.toEntity()) } Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to create or update addon item") } } catch (e: Exception) { @@ -126,28 +131,28 @@ class AddOnItemRepositoryImpl( } override suspend fun validateItemName(name: String, addOnItemId: Int?): ValidationResult { - if(name.isEmpty()) { + if (name.isEmpty()) { return ValidationResult( successful = false, - errorMessage = AddOnConstants.ADDON_NAME_EMPTY_ERROR, + errorMessage = AddOnTestTags.ADDON_NAME_EMPTY_ERROR, ) } if (name.length < 5) { return ValidationResult( successful = false, - errorMessage = AddOnConstants.ADDON_NAME_LENGTH_ERROR, + errorMessage = AddOnTestTags.ADDON_NAME_LENGTH_ERROR, ) } - if (!name.startsWith(AddOnConstants.ADDON_WHITELIST_ITEM)) { + if (!name.startsWith(AddOnTestTags.ADDON_WHITELIST_ITEM)) { val result = name.any { it.isDigit() } if (result) { return ValidationResult( successful = false, - errorMessage = AddOnConstants.ADDON_NAME_DIGIT_ERROR, + errorMessage = AddOnTestTags.ADDON_NAME_DIGIT_ERROR, ) } @@ -158,7 +163,7 @@ class AddOnItemRepositoryImpl( if (serverResult) { return ValidationResult( successful = false, - errorMessage = AddOnConstants.ADDON_NAME_ALREADY_EXIST_ERROR, + errorMessage = AddOnTestTags.ADDON_NAME_ALREADY_EXIST_ERROR, ) } } @@ -169,17 +174,17 @@ class AddOnItemRepositoryImpl( } override fun validateItemPrice(price: Int): ValidationResult { - if(price == 0) { + if (price == 0) { return ValidationResult( successful = false, - errorMessage = AddOnConstants.ADDON_PRICE_EMPTY_ERROR, + errorMessage = AddOnTestTags.ADDON_PRICE_EMPTY_ERROR, ) } if (price < 5) { return ValidationResult( successful = false, - errorMessage = AddOnConstants.ADDON_PRICE_LESS_THAN_FIVE_ERROR, + errorMessage = AddOnTestTags.ADDON_PRICE_LESS_THAN_FIVE_ERROR, ) } diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/data/repository/AddressRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/AddressRepositoryImpl.kt similarity index 66% rename from app/src/main/java/com/niyaj/poposroom/features/address/data/repository/AddressRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/AddressRepositoryImpl.kt index 2dfdd351..0add1ad7 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/data/repository/AddressRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/AddressRepositoryImpl.kt @@ -1,15 +1,17 @@ -package com.niyaj.poposroom.features.address.data.repository - -import com.niyaj.poposroom.features.address.data.dao.AddressDao -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.address.domain.model.searchAddress -import com.niyaj.poposroom.features.address.domain.repository.AddressRepository -import com.niyaj.poposroom.features.address.domain.repository.AddressValidationRepository -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.AddressRepository +import com.niyaj.data.repository.validation.AddressValidationRepository +import com.niyaj.database.dao.AddressDao +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Address +import com.niyaj.model.searchAddress +import com.niyaj.data.utils.AddressTestTags import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -19,20 +21,21 @@ import kotlinx.coroutines.withContext class AddressRepositoryImpl( private val addressDao: AddressDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : AddressRepository, AddressValidationRepository { @OptIn(ExperimentalCoroutinesApi::class) override suspend fun getAllAddress(searchText: String): Flow> { return withContext(ioDispatcher) { - addressDao.getAllAddresses().mapLatest { it.searchAddress(searchText) } + addressDao.getAllAddresses() + .mapLatest { list -> list.map { it.asExternalModel() }.searchAddress(searchText) } } } override suspend fun getAddressById(addressId: Int): Resource { return try { - withContext(ioDispatcher){ - Resource.Success(addressDao.getAddressById(addressId)) + withContext(ioDispatcher) { + Resource.Success(addressDao.getAddressById(addressId)?.asExternalModel()) } } catch (e: Exception) { Resource.Error(e.message ?: "Unable to new address") @@ -41,15 +44,16 @@ class AddressRepositoryImpl( override suspend fun addOrIgnoreAddress(newAddress: Address): Int { return try { - withContext(ioDispatcher){ + withContext(ioDispatcher) { val validateAddressName = validateAddressName(newAddress.addressName) val validateAddressShortName = validateAddressShortName(newAddress.shortName) - val hasError = listOf(validateAddressName, validateAddressShortName).any { !it.successful} + val hasError = + listOf(validateAddressName, validateAddressShortName).any { !it.successful } if (!hasError) { - addressDao.insertOrIgnoreAddress(newAddress).toInt() - }else { + addressDao.insertOrIgnoreAddress(newAddress.toEntity()).toInt() + } else { 0 } } @@ -60,17 +64,19 @@ class AddressRepositoryImpl( override suspend fun updateAddress(newAddress: Address): Resource { return try { - withContext(ioDispatcher){ - val validateAddressName = validateAddressName(newAddress.addressName, newAddress.addressId) + withContext(ioDispatcher) { + val validateAddressName = + validateAddressName(newAddress.addressName, newAddress.addressId) val validateAddressShortName = validateAddressShortName(newAddress.shortName) - val hasError = listOf(validateAddressName, validateAddressShortName).any { !it.successful} + val hasError = + listOf(validateAddressName, validateAddressShortName).any { !it.successful } if (!hasError) { - val result = addressDao.updateAddress(newAddress) + val result = addressDao.updateAddress(newAddress.toEntity()) Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to update address") } } @@ -81,17 +87,19 @@ class AddressRepositoryImpl( override suspend fun upsertAddress(newAddress: Address): Resource { return try { - withContext(ioDispatcher){ - val validateAddressName = validateAddressName(newAddress.addressName, newAddress.addressId) + withContext(ioDispatcher) { + val validateAddressName = + validateAddressName(newAddress.addressName, newAddress.addressId) val validateAddressShortName = validateAddressShortName(newAddress.shortName) - val hasError = listOf(validateAddressName, validateAddressShortName).any { !it.successful} + val hasError = + listOf(validateAddressName, validateAddressShortName).any { !it.successful } if (!hasError) { - val result = addressDao.upsertAddress(newAddress) + val result = addressDao.upsertAddress(newAddress.toEntity()) Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to create or update address") } } @@ -102,7 +110,7 @@ class AddressRepositoryImpl( override suspend fun deleteAddress(addressId: Int): Resource { return try { - withContext(ioDispatcher){ + withContext(ioDispatcher) { val result = addressDao.deleteAddress(addressId) Resource.Success(result > 0) @@ -114,7 +122,7 @@ class AddressRepositoryImpl( override suspend fun deleteAddresses(addressIds: List): Resource { return try { - withContext(ioDispatcher){ + withContext(ioDispatcher) { val result = addressDao.deleteAddresses(addressIds) Resource.Success(result > 0) @@ -124,15 +132,18 @@ class AddressRepositoryImpl( } } - override suspend fun validateAddressName(addressName: String, addressId: Int?): ValidationResult { - if(addressName.isEmpty()) { + override suspend fun validateAddressName( + addressName: String, + addressId: Int?, + ): ValidationResult { + if (addressName.isEmpty()) { return ValidationResult( successful = false, errorMessage = AddressTestTags.ADDRESS_NAME_EMPTY_ERROR, ) } - if(addressName.length < 2) { + if (addressName.length < 2) { return ValidationResult( successful = false, errorMessage = AddressTestTags.ADDRESS_NAME_LENGTH_ERROR @@ -156,14 +167,14 @@ class AddressRepositoryImpl( } override fun validateAddressShortName(addressShortName: String): ValidationResult { - if(addressShortName.isEmpty()) { + if (addressShortName.isEmpty()) { return ValidationResult( successful = false, errorMessage = AddressTestTags.ADDRESS_SHORT_NAME_EMPTY_ERROR ) } - if(addressShortName.length < 2) { + if (addressShortName.length < 2) { return ValidationResult( successful = false, errorMessage = AddressTestTags.ADDRESS_PRICE_LESS_THAN_TWO_ERROR diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/data/repository/CartOrderRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/CartOrderRepositoryImpl.kt similarity index 79% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/data/repository/CartOrderRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/CartOrderRepositoryImpl.kt index 3dc6b2b4..ef47c889 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/data/repository/CartOrderRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/CartOrderRepositoryImpl.kt @@ -1,38 +1,43 @@ -package com.niyaj.poposroom.features.cart_order.data.repository - -import com.niyaj.poposroom.features.address.data.dao.AddressDao -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.address.domain.model.searchAddress -import com.niyaj.poposroom.features.cart_order.data.dao.CartOrderDao -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrder -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrderEntity -import com.niyaj.poposroom.features.cart_order.domain.model.filterCartOrder -import com.niyaj.poposroom.features.cart_order.domain.repository.CartOrderRepository -import com.niyaj.poposroom.features.cart_order.domain.repository.CartOrderValidationRepository -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.ADDRESS_NAME_LENGTH_ERROR -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CART_ORDER_NAME_EMPTY_ERROR -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CART_ORDER_NAME_ERROR -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CART_ORDER_PHONE_EMPTY_ERROR -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CART_ORDER_PHONE_ERROR -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CUSTOMER_PHONE_LENGTH_ERROR -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CUSTOMER_PHONE_LETTER_ERROR -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.ORDER_PRICE_LESS_THAN_TWO_ERROR -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.ORDER_SHORT_NAME_EMPTY_ERROR -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderStatus -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult -import com.niyaj.poposroom.features.customer.data.dao.CustomerDao -import com.niyaj.poposroom.features.customer.domain.model.Customer -import com.niyaj.poposroom.features.customer.domain.model.searchCustomer -import com.niyaj.poposroom.features.selected.data.dao.SelectedDao -import com.niyaj.poposroom.features.selected.domain.model.Selected +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.CartOrderRepository +import com.niyaj.data.repository.validation.CartOrderValidationRepository +import com.niyaj.data.utils.CartOrderTestTags.ADDRESS_NAME_LENGTH_ERROR +import com.niyaj.data.utils.CartOrderTestTags.CART_ORDER_NAME_EMPTY_ERROR +import com.niyaj.data.utils.CartOrderTestTags.CART_ORDER_NAME_ERROR +import com.niyaj.data.utils.CartOrderTestTags.CART_ORDER_PHONE_EMPTY_ERROR +import com.niyaj.data.utils.CartOrderTestTags.CART_ORDER_PHONE_ERROR +import com.niyaj.data.utils.CartOrderTestTags.CUSTOMER_PHONE_LENGTH_ERROR +import com.niyaj.data.utils.CartOrderTestTags.CUSTOMER_PHONE_LETTER_ERROR +import com.niyaj.data.utils.CartOrderTestTags.ORDER_PRICE_LESS_THAN_TWO_ERROR +import com.niyaj.data.utils.CartOrderTestTags.ORDER_SHORT_NAME_EMPTY_ERROR +import com.niyaj.database.dao.AddressDao +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.CustomerDao +import com.niyaj.database.dao.SelectedDao +import com.niyaj.database.model.CartOrderEntity +import com.niyaj.database.model.SelectedEntity +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Address +import com.niyaj.model.CartOrder +import com.niyaj.model.Customer +import com.niyaj.model.OrderStatus +import com.niyaj.model.OrderType +import com.niyaj.model.SELECTED_ID +import com.niyaj.model.Selected +import com.niyaj.model.filterCartOrder +import com.niyaj.model.searchAddress +import com.niyaj.model.searchCustomer import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.withContext @@ -42,7 +47,7 @@ class CartOrderRepositoryImpl( private val addressDao: AddressDao, private val selectedDao: SelectedDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : CartOrderRepository, CartOrderValidationRepository { @OptIn(ExperimentalCoroutinesApi::class) @@ -52,13 +57,13 @@ class CartOrderRepositoryImpl( list.map { cartOrder -> val address = if (cartOrder.orderType != OrderType.DineIn) { withContext(ioDispatcher) { - addressDao.getAddressById(cartOrder.addressId) + addressDao.getAddressById(cartOrder.addressId)?.asExternalModel() } } else null val customer = if (cartOrder.orderType != OrderType.DineIn) { withContext(ioDispatcher) { - customerDao.getCustomerById(cartOrder.customerId) + customerDao.getCustomerById(cartOrder.customerId)?.asExternalModel() } } else null @@ -78,16 +83,18 @@ class CartOrderRepositoryImpl( } override fun getSelectedCartOrder(): Flow { - return selectedDao.getSelectedCartOrder() + return selectedDao.getSelectedCartOrder().map { + it?.asExternalModel() + } } override suspend fun insertOrUpdateSelectedOrder(selected: Selected): Resource { return withContext(ioDispatcher) { try { - val result = selectedDao.insertOrUpdateSelectedOrder(selected) + val result = selectedDao.insertOrUpdateSelectedOrder(selected.toEntity()) Resource.Success(result > 0) - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message) } } @@ -96,19 +103,30 @@ class CartOrderRepositoryImpl( @OptIn(ExperimentalCoroutinesApi::class) override suspend fun getAllAddresses(searchText: String): Flow> { return withContext(ioDispatcher) { - addressDao.getAllAddresses().mapLatest { it.searchAddress(searchText) } + addressDao.getAllAddresses().mapLatest { it -> + it.map { + it.asExternalModel() + }.searchAddress(searchText) + } } } @OptIn(ExperimentalCoroutinesApi::class) override suspend fun getAllCustomer(searchText: String): Flow> { return withContext(ioDispatcher) { - customerDao.getAllCustomer().mapLatest { it.searchCustomer(searchText) } + customerDao.getAllCustomer().mapLatest { it -> + it.map { + it.asExternalModel() + }.searchCustomer(searchText) + } } } @OptIn(ExperimentalCoroutinesApi::class) - override suspend fun getAllCartOrders(searchText: String): Flow> { + override suspend fun getAllCartOrders( + searchText: String, + viewAll: Boolean, + ): Flow> { withContext(ioDispatcher) { async { updateOrIgnoreSelectedOrder() @@ -116,17 +134,21 @@ class CartOrderRepositoryImpl( } return withContext(ioDispatcher) { - cartOrderDao.getAllCartOrders().mapLatest { list -> + val result = if (viewAll) { + cartOrderDao.getAllCartOrders() + } else cartOrderDao.getProcessingCartOrders() + + result.mapLatest { list -> list.map { cartOrder -> val address = if (cartOrder.orderType != OrderType.DineIn) { withContext(ioDispatcher) { - addressDao.getAddressById(cartOrder.addressId) + addressDao.getAddressById(cartOrder.addressId)?.asExternalModel() } } else null val customer = if (cartOrder.orderType != OrderType.DineIn) { withContext(ioDispatcher) { - customerDao.getCustomerById(cartOrder.customerId) + customerDao.getCustomerById(cartOrder.customerId)?.asExternalModel() } } else null @@ -155,13 +177,13 @@ class CartOrderRepositoryImpl( val cartOrder = result?.let { cartOrder -> val address = if (cartOrder.orderType != OrderType.DineIn) { withContext(ioDispatcher) { - addressDao.getAddressById(cartOrder.addressId) + addressDao.getAddressById(cartOrder.addressId)?.asExternalModel() } } else null val customer = if (cartOrder.orderType != OrderType.DineIn) { withContext(ioDispatcher) { - customerDao.getCustomerById(cartOrder.customerId) + customerDao.getCustomerById(cartOrder.customerId)?.asExternalModel() } } else null @@ -206,7 +228,7 @@ class CartOrderRepositoryImpl( if (!hasError) { withContext(ioDispatcher) { addressDao.getAddressByName(newAddress.addressName) - ?: addressDao.insertOrIgnoreAddress(newAddress).toInt() + ?: addressDao.insertOrIgnoreAddress(newAddress.toEntity()).toInt() } } else { 0 @@ -224,7 +246,7 @@ class CartOrderRepositoryImpl( if (validateCustomerPhone.successful) { customerDao.getCustomerByPhone(newCustomer.customerPhone) - ?: customerDao.insertOrIgnoreCustomer(newCustomer).toInt() + ?: customerDao.insertOrIgnoreCustomer(newCustomer.toEntity()).toInt() } else { 0 } @@ -279,7 +301,10 @@ class CartOrderRepositoryImpl( if (result > 0) { async(ioDispatcher) { selectedDao.insertOrUpdateSelectedOrder( - Selected(orderId = result.toInt()) + SelectedEntity( + selectedId = SELECTED_ID, + orderId = result.toInt() + ) ) }.await() } @@ -452,8 +477,13 @@ class CartOrderRepositoryImpl( val lastId = cartOrderDao.getLastProcessingId() lastId?.let { - selectedDao.insertOrUpdateSelectedOrder(Selected(orderId = it)) - } ?: selectedDao.deleteSelectedOrder() + selectedDao.insertOrUpdateSelectedOrder( + SelectedEntity( + selectedId = SELECTED_ID, + orderId = it + ) + ) + } ?: selectedDao.deleteSelectedOrder(SELECTED_ID) } } @@ -465,19 +495,30 @@ class CartOrderRepositoryImpl( val status = cartOrderDao.getOrderStatus(currentId) if (status == OrderStatus.PLACED) { - selectedDao.deleteSelectedOrder() + selectedDao.deleteSelectedOrder(SELECTED_ID) val lastId = cartOrderDao.getLastProcessingId() lastId?.let { - selectedDao.insertOrUpdateSelectedOrder(Selected(orderId = it)) + selectedDao.insertOrUpdateSelectedOrder( + SelectedEntity( + SELECTED_ID, + orderId = it + ) + ) } - } else {} - }else { + } else { + } + } else { val lastId = cartOrderDao.getLastProcessingId() lastId?.let { - selectedDao.insertOrUpdateSelectedOrder(Selected(orderId = it)) + selectedDao.insertOrUpdateSelectedOrder( + SelectedEntity( + SELECTED_ID, + orderId = it + ) + ) } } } diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/data/repository/CartRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/CartRepositoryImpl.kt similarity index 86% rename from app/src/main/java/com/niyaj/poposroom/features/cart/data/repository/CartRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/CartRepositoryImpl.kt index f1437b8e..e7718f8d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/data/repository/CartRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/CartRepositoryImpl.kt @@ -1,22 +1,27 @@ -package com.niyaj.poposroom.features.cart.data.repository - -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.cart.data.dao.CartDao -import com.niyaj.poposroom.features.cart.domain.model.CartEntity -import com.niyaj.poposroom.features.cart.domain.model.CartItem -import com.niyaj.poposroom.features.cart.domain.model.CartItems -import com.niyaj.poposroom.features.cart.domain.model.CartProductItem -import com.niyaj.poposroom.features.cart.domain.model.OrderPrice -import com.niyaj.poposroom.features.cart.domain.model.OrderWithCart -import com.niyaj.poposroom.features.cart.domain.repository.CartRepository -import com.niyaj.poposroom.features.cart_order.data.dao.CartOrderDao -import com.niyaj.poposroom.features.cart_order.domain.model.CartAddOnItems -import com.niyaj.poposroom.features.cart_order.domain.model.CartCharges -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.toTimeSpan +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.toTimeSpan +import com.niyaj.data.repository.CartRepository +import com.niyaj.database.dao.CartDao +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.SelectedDao +import com.niyaj.database.model.CartAddOnItemsEntity +import com.niyaj.database.model.CartChargesEntity +import com.niyaj.database.model.CartEntity +import com.niyaj.database.model.OrderWithCartDto +import com.niyaj.database.model.SelectedEntity +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.AddOnItem +import com.niyaj.model.CartItem +import com.niyaj.model.CartItems +import com.niyaj.model.CartProductItem +import com.niyaj.model.OrderPrice +import com.niyaj.model.OrderType +import com.niyaj.model.OrderWithCartItems +import com.niyaj.model.SELECTED_ID import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async @@ -25,6 +30,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext @@ -32,18 +38,21 @@ import kotlinx.coroutines.withContext class CartRepositoryImpl( private val cartDao: CartDao, private val cartOrderDao: CartOrderDao, + private val selectedDao: SelectedDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : CartRepository { override fun getAllAddOnItems(): Flow> { - return cartDao.getAllAddOnItems() + return cartDao.getAllAddOnItems().mapLatest { list -> + list.map { + it.asExternalModel() + } + } } - override suspend fun getAllCartOrders(): Flow> { - return withContext(ioDispatcher) { - cartDao.getAllCartOrders() - } + override suspend fun getAllCartOrders(): Flow> { + return flow { } } @OptIn(ExperimentalCoroutinesApi::class) @@ -128,6 +137,10 @@ class CartRepositoryImpl( withContext(ioDispatcher) { val result = cartOrderDao.placeOrder(orderId) + async { + updateOrDeleteSelectedOrder() + }.await() + Resource.Success(result > 0) } } catch (e: Exception) { @@ -140,6 +153,10 @@ class CartRepositoryImpl( withContext(ioDispatcher) { val result = cartOrderDao.placeAllOrder(orderIds) + async { + updateOrDeleteSelectedOrder() + }.await() + Resource.Success(result > 0) } } catch (e: Exception) { @@ -155,7 +172,7 @@ class CartRepositoryImpl( val result = if (item != null) { cartOrderDao.deleteCartAddOnItem(orderId, itemId) } else { - cartOrderDao.insertCartAddOnItem(CartAddOnItems(orderId, itemId)).toInt() + cartOrderDao.insertCartAddOnItem(CartAddOnItemsEntity(orderId, itemId)).toInt() } Resource.Success(result > 0) @@ -173,7 +190,7 @@ class CartRepositoryImpl( val result = if (item != null) { cartOrderDao.deleteCartCharges(orderId, chargesId) } else { - cartOrderDao.insertCartCharge(CartCharges(orderId, chargesId)).toInt() + cartOrderDao.insertCartCharge(CartChargesEntity(orderId, chargesId)).toInt() } Resource.Success(result > 0) @@ -257,7 +274,7 @@ class CartRepositoryImpl( } - private suspend fun mapCartOrderToCartItem(cartOrders: List): List { + private suspend fun mapCartOrderToCartItem(cartOrders: List): List { return coroutineScope { cartOrders.map { order -> @@ -294,7 +311,7 @@ class CartRepositoryImpl( } else null } - val orderPrice = addOnItems.await().combine(charges.await()){ _, _ -> + val orderPrice = addOnItems.await().combine(charges.await()) { _, _ -> countTotalPrice( order.cartOrder.orderId, order.cartOrder.doesChargesIncluded, @@ -318,7 +335,7 @@ class CartRepositoryImpl( } @OptIn(ExperimentalCoroutinesApi::class) - private suspend fun mapCartOrderToCartItems(cartOrders: List): List { + private suspend fun mapCartOrderToCartItems(cartOrders: List): List { return coroutineScope { cartOrders.map { order -> @@ -409,4 +426,19 @@ class CartRepositoryImpl( } } + private suspend fun updateOrDeleteSelectedOrder() { + withContext(ioDispatcher) { + val lastId = cartOrderDao.getLastProcessingId() + + lastId?.let { + selectedDao.insertOrUpdateSelectedOrder( + SelectedEntity( + selectedId = SELECTED_ID, + orderId = it + ) + ) + } ?: selectedDao.deleteSelectedOrder(SELECTED_ID) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/data/repository/CategoryRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/CategoryRepositoryImpl.kt similarity index 76% rename from app/src/main/java/com/niyaj/poposroom/features/category/data/repository/CategoryRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/CategoryRepositoryImpl.kt index f2f0e49a..9fa30ad2 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/data/repository/CategoryRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/CategoryRepositoryImpl.kt @@ -1,15 +1,17 @@ -package com.niyaj.poposroom.features.category.data.repository - -import com.niyaj.poposroom.features.category.data.dao.CategoryDao -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.category.domain.model.searchCategory -import com.niyaj.poposroom.features.category.domain.repository.CategoryRepository -import com.niyaj.poposroom.features.category.domain.repository.CategoryValidationRepository -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.CategoryRepository +import com.niyaj.data.repository.validation.CategoryValidationRepository +import com.niyaj.data.utils.CategoryConstants +import com.niyaj.database.dao.CategoryDao +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Category +import com.niyaj.model.searchCategory import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -19,22 +21,26 @@ import kotlinx.coroutines.withContext class CategoryRepositoryImpl( private val categoryDao: CategoryDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : CategoryRepository, CategoryValidationRepository { @OptIn(ExperimentalCoroutinesApi::class) override suspend fun getAllCategory(searchText: String): Flow> { return withContext(ioDispatcher) { - categoryDao.getAllCategories().mapLatest { it.searchCategory(searchText) } + categoryDao.getAllCategories().mapLatest { it -> + it.map { + it.asExternalModel() + }.searchCategory(searchText) + } } } override suspend fun getCategoryById(categoryId: Int): Resource { return try { withContext(ioDispatcher) { - Resource.Success(categoryDao.getCategoryById(categoryId)) + Resource.Success(categoryDao.getCategoryById(categoryId)?.asExternalModel()) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message) } } @@ -45,7 +51,7 @@ class CategoryRepositoryImpl( val validateCategoryName = validateCategoryName(newCategory.categoryName) if (validateCategoryName.successful) { - val result = categoryDao.insertOrIgnoreCategory(newCategory) + val result = categoryDao.insertOrIgnoreCategory(newCategory.toEntity()) Resource.Success(result > 0) } else { @@ -60,10 +66,11 @@ class CategoryRepositoryImpl( override suspend fun updateCategory(newCategory: Category): Resource { return try { withContext(ioDispatcher) { - val validateCategoryName = validateCategoryName(newCategory.categoryName, newCategory.categoryId) + val validateCategoryName = + validateCategoryName(newCategory.categoryName, newCategory.categoryId) if (validateCategoryName.successful) { - val result = categoryDao.updateCategory(newCategory) + val result = categoryDao.updateCategory(newCategory.toEntity()) Resource.Success(result > 0) } else { @@ -78,10 +85,11 @@ class CategoryRepositoryImpl( override suspend fun upsertCategory(newCategory: Category): Resource { return try { withContext(ioDispatcher) { - val validateCategoryName = validateCategoryName(newCategory.categoryName, newCategory.categoryId) + val validateCategoryName = + validateCategoryName(newCategory.categoryName, newCategory.categoryId) if (validateCategoryName.successful) { - val result = categoryDao.upsertCategory(newCategory) + val result = categoryDao.upsertCategory(newCategory.toEntity()) Resource.Success(result > 0) } else { @@ -121,16 +129,16 @@ class CategoryRepositoryImpl( override suspend fun validateCategoryName( categoryName: String, - categoryId: Int? + categoryId: Int?, ): ValidationResult { - if(categoryName.isEmpty()){ + if (categoryName.isEmpty()) { return ValidationResult( successful = false, errorMessage = CategoryConstants.CATEGORY_NAME_EMPTY_ERROR ) } - if(categoryName.length < 3) { + if (categoryName.length < 3) { return ValidationResult( successful = false, errorMessage = CategoryConstants.CATEGORY_NAME_LENGTH_ERROR @@ -141,7 +149,7 @@ class CategoryRepositoryImpl( categoryDao.findCategoryByName(categoryName, categoryId) != null } - if(serverResult) { + if (serverResult) { return ValidationResult( successful = false, errorMessage = CategoryConstants.CATEGORY_NAME_ALREADY_EXIST_ERROR diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/data/repository/ChargesRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/ChargesRepositoryImpl.kt similarity index 88% rename from app/src/main/java/com/niyaj/poposroom/features/charges/data/repository/ChargesRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/ChargesRepositoryImpl.kt index c20875fd..f098cb34 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/data/repository/ChargesRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/ChargesRepositoryImpl.kt @@ -1,15 +1,17 @@ -package com.niyaj.poposroom.features.charges.data.repository - +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.ChargesRepository +import com.niyaj.data.repository.validation.ChargesValidationRepository +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Charges +import com.niyaj.model.searchCharges import com.niyaj.poposroom.features.charges.data.dao.ChargesDao -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.charges.domain.model.searchCharges -import com.niyaj.poposroom.features.charges.domain.repository.ChargesRepository -import com.niyaj.poposroom.features.charges.domain.repository.ChargesValidationRepository -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.data.utils.ChargesTestTags import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -25,14 +27,18 @@ class ChargesRepositoryImpl( @OptIn(ExperimentalCoroutinesApi::class) override suspend fun getAllCharges(searchText: String): Flow> { return withContext(ioDispatcher) { - chargesDao.getAllCharges().mapLatest { it.searchCharges(searchText) } + chargesDao.getAllCharges().mapLatest { it-> + it.map { + it.asExternalModel() + }.searchCharges(searchText) + } } } override suspend fun getChargesById(chargesId: Int): Resource { return try { withContext(ioDispatcher) { - Resource.Success(chargesDao.getChargesById(chargesId)) + Resource.Success(chargesDao.getChargesById(chargesId)?.asExternalModel()) } }catch (e: Exception) { Resource.Error(e.message) @@ -48,7 +54,7 @@ class ChargesRepositoryImpl( if (!hasError) { withContext(ioDispatcher){ - val result = chargesDao.insertOrIgnoreCharges(newCharges) + val result = chargesDao.insertOrIgnoreCharges(newCharges.toEntity()) Resource.Success(result > 0) } @@ -69,7 +75,7 @@ class ChargesRepositoryImpl( if (!hasError) { withContext(ioDispatcher){ - val result = chargesDao.updateCharges(newCharges) + val result = chargesDao.updateCharges(newCharges.toEntity()) Resource.Success(result > 0) } @@ -90,7 +96,7 @@ class ChargesRepositoryImpl( if (!hasError) { withContext(ioDispatcher){ - val result = chargesDao.upsertCharges(newCharges) + val result = chargesDao.upsertCharges(newCharges.toEntity()) Resource.Success(result > 0) } diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/data/repository/CustomerRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/CustomerRepositoryImpl.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/customer/data/repository/CustomerRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/CustomerRepositoryImpl.kt index f2f102f5..2689b37b 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/data/repository/CustomerRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/CustomerRepositoryImpl.kt @@ -1,16 +1,18 @@ -package com.niyaj.poposroom.features.customer.data.repository +package com.niyaj.data.data.repository import android.util.Patterns -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult -import com.niyaj.poposroom.features.customer.data.dao.CustomerDao -import com.niyaj.poposroom.features.customer.domain.model.Customer -import com.niyaj.poposroom.features.customer.domain.model.searchCustomer -import com.niyaj.poposroom.features.customer.domain.repository.CustomerRepository -import com.niyaj.poposroom.features.customer.domain.repository.CustomerValidationRepository -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.CustomerRepository +import com.niyaj.data.repository.validation.CustomerValidationRepository +import com.niyaj.database.dao.CustomerDao +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Customer +import com.niyaj.model.searchCustomer +import com.niyaj.data.utils.CustomerTestTags import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -20,22 +22,26 @@ import kotlinx.coroutines.withContext class CustomerRepositoryImpl( private val customerDao: CustomerDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : CustomerRepository, CustomerValidationRepository { @OptIn(ExperimentalCoroutinesApi::class) override suspend fun getAllCustomer(searchText: String): Flow> { return withContext(ioDispatcher) { - customerDao.getAllCustomer().mapLatest { it.searchCustomer(searchText) } + customerDao.getAllCustomer().mapLatest { it -> + it.map { + it.asExternalModel() + }.searchCustomer(searchText) + } } } override suspend fun getCustomerById(customerId: Int): Resource { return try { withContext(ioDispatcher) { - Resource.Success(customerDao.getCustomerById(customerId)) + Resource.Success(customerDao.getCustomerById(customerId)?.asExternalModel()) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message) } } @@ -53,10 +59,10 @@ class CustomerRepositoryImpl( ).any { !it.successful } if (!hasError) { - withContext(ioDispatcher){ - customerDao.insertOrIgnoreCustomer(newCustomer).toInt() + withContext(ioDispatcher) { + customerDao.insertOrIgnoreCustomer(newCustomer.toEntity()).toInt() } - }else { + } else { 0 } } catch (e: Exception) { @@ -67,7 +73,8 @@ class CustomerRepositoryImpl( override suspend fun updateCustomer(newCustomer: Customer): Resource { return try { val validateCustomerName = validateCustomerName(newCustomer.customerName) - val validateCustomerPhone = validateCustomerPhone(newCustomer.customerPhone, newCustomer.customerId) + val validateCustomerPhone = + validateCustomerPhone(newCustomer.customerPhone, newCustomer.customerId) val validateCustomerEmail = validateCustomerEmail(newCustomer.customerEmail) val hasError = listOf( @@ -77,12 +84,12 @@ class CustomerRepositoryImpl( ).any { !it.successful } if (!hasError) { - withContext(ioDispatcher){ - val result = customerDao.updateCustomer(newCustomer) + withContext(ioDispatcher) { + val result = customerDao.updateCustomer(newCustomer.toEntity()) Resource.Success(result > 0) } - }else { + } else { Resource.Error("Unable to validate customer") } } catch (e: Exception) { @@ -93,7 +100,8 @@ class CustomerRepositoryImpl( override suspend fun upsertCustomer(newCustomer: Customer): Resource { return try { val validateCustomerName = validateCustomerName(newCustomer.customerName) - val validateCustomerPhone = validateCustomerPhone(newCustomer.customerPhone, newCustomer.customerId) + val validateCustomerPhone = + validateCustomerPhone(newCustomer.customerPhone, newCustomer.customerId) val validateCustomerEmail = validateCustomerEmail(newCustomer.customerEmail) val hasError = listOf( @@ -103,12 +111,12 @@ class CustomerRepositoryImpl( ).any { !it.successful } if (!hasError) { - withContext(ioDispatcher){ - val result = customerDao.upsertCustomer(newCustomer) + withContext(ioDispatcher) { + val result = customerDao.upsertCustomer(newCustomer.toEntity()) Resource.Success(result > 0) } - }else { + } else { Resource.Error("Unable to validate customer") } } catch (e: Exception) { @@ -123,7 +131,7 @@ class CustomerRepositoryImpl( Resource.Success(result > 0) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message ?: "Unable to delete customer") } } @@ -135,14 +143,14 @@ class CustomerRepositoryImpl( Resource.Success(result > 0) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message ?: "Unable to delete customers") } } override fun validateCustomerName(customerName: String?): ValidationResult { - if(!customerName.isNullOrEmpty()) { - if(customerName.length < 3) { + if (!customerName.isNullOrEmpty()) { + if (customerName.length < 3) { return ValidationResult( successful = false, errorMessage = CustomerTestTags.CUSTOMER_NAME_LENGTH_ERROR, @@ -156,8 +164,8 @@ class CustomerRepositoryImpl( } override fun validateCustomerEmail(customerEmail: String?): ValidationResult { - if(!customerEmail.isNullOrEmpty()) { - if(!Patterns.EMAIL_ADDRESS.matcher(customerEmail).matches()) { + if (!customerEmail.isNullOrEmpty()) { + if (!Patterns.EMAIL_ADDRESS.matcher(customerEmail).matches()) { return ValidationResult( successful = false, errorMessage = CustomerTestTags.CUSTOMER_EMAIL_VALID_ERROR, @@ -172,16 +180,16 @@ class CustomerRepositoryImpl( override suspend fun validateCustomerPhone( customerPhone: String, - customerId: Int? + customerId: Int?, ): ValidationResult { - if(customerPhone.isEmpty()) { + if (customerPhone.isEmpty()) { return ValidationResult( successful = false, errorMessage = CustomerTestTags.CUSTOMER_PHONE_EMPTY_ERROR, ) } - if(customerPhone.length < 10 || customerPhone.length > 10) { + if (customerPhone.length < 10 || customerPhone.length > 10) { return ValidationResult( successful = false, errorMessage = CustomerTestTags.CUSTOMER_PHONE_LENGTH_ERROR, @@ -190,7 +198,7 @@ class CustomerRepositoryImpl( val containsLetters = customerPhone.any { it.isLetter() } - if(containsLetters){ + if (containsLetters) { return ValidationResult( successful = false, errorMessage = CustomerTestTags.CUSTOMER_PHONE_LETTER_ERROR @@ -201,7 +209,7 @@ class CustomerRepositoryImpl( customerDao.findCustomerByPhone(customerPhone, customerId) != null } - if(serverResult){ + if (serverResult) { return ValidationResult( successful = false, errorMessage = CustomerTestTags.CUSTOMER_PHONE_ALREADY_EXIST_ERROR diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/data/repository/EmployeeRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/EmployeeRepositoryImpl.kt similarity index 70% rename from app/src/main/java/com/niyaj/poposroom/features/employee/data/repository/EmployeeRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/EmployeeRepositoryImpl.kt index 458f01de..594a68ce 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/data/repository/EmployeeRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/EmployeeRepositoryImpl.kt @@ -1,15 +1,17 @@ -package com.niyaj.poposroom.features.employee.data.repository - -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult -import com.niyaj.poposroom.features.employee.data.dao.EmployeeDao -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee.domain.model.searchEmployee -import com.niyaj.poposroom.features.employee.domain.repository.EmployeeRepository -import com.niyaj.poposroom.features.employee.domain.repository.EmployeeValidationRepository -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.EmployeeRepository +import com.niyaj.data.repository.EmployeeValidationRepository +import com.niyaj.database.dao.EmployeeDao +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Employee +import com.niyaj.model.searchEmployee +import com.niyaj.data.utils.EmployeeTestTags import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -19,22 +21,26 @@ import kotlinx.coroutines.withContext class EmployeeRepositoryImpl( private val employeeDao: EmployeeDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : EmployeeRepository, EmployeeValidationRepository { @OptIn(ExperimentalCoroutinesApi::class) override suspend fun getAllEmployee(searchText: String): Flow> { return withContext(ioDispatcher) { - employeeDao.getAllEmployee().mapLatest { it.searchEmployee(searchText) } + employeeDao.getAllEmployee().mapLatest { it -> + it.map { + it.asExternalModel() + }.searchEmployee(searchText) + } } } override suspend fun getEmployeeById(employeeId: Int): Resource { return try { withContext(ioDispatcher) { - Resource.Success(employeeDao.getEmployeeById(employeeId)) + Resource.Success(employeeDao.getEmployeeById(employeeId)?.asExternalModel()) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message) } } @@ -44,17 +50,23 @@ class EmployeeRepositoryImpl( withContext(ioDispatcher) { val validateEmployeeName = validateEmployeeName(newEmployee.employeeName) val validateEmployeePhone = validateEmployeePhone(newEmployee.employeePhone) - val validateEmployeePosition = validateEmployeePosition(newEmployee.employeePosition) + val validateEmployeePosition = + validateEmployeePosition(newEmployee.employeePosition) val validateEmployeeSalary = validateEmployeeSalary(newEmployee.employeeSalary) - val hasError = listOf(validateEmployeeName, validateEmployeePhone, validateEmployeePosition, validateEmployeeSalary).any { !it.successful } + val hasError = listOf( + validateEmployeeName, + validateEmployeePhone, + validateEmployeePosition, + validateEmployeeSalary + ).any { !it.successful } if (!hasError) { withContext(ioDispatcher) { - val result = employeeDao.insertOrIgnoreEmployee(newEmployee) + val result = employeeDao.insertOrIgnoreEmployee(newEmployee.toEntity()) Resource.Success(result > 0) } - }else { + } else { Resource.Error("Unable to validate employee") } } @@ -67,18 +79,25 @@ class EmployeeRepositoryImpl( return try { withContext(ioDispatcher) { val validateEmployeeName = validateEmployeeName(newEmployee.employeeName) - val validateEmployeePhone = validateEmployeePhone(newEmployee.employeePhone, newEmployee.employeeId) - val validateEmployeePosition = validateEmployeePosition(newEmployee.employeePosition) + val validateEmployeePhone = + validateEmployeePhone(newEmployee.employeePhone, newEmployee.employeeId) + val validateEmployeePosition = + validateEmployeePosition(newEmployee.employeePosition) val validateEmployeeSalary = validateEmployeeSalary(newEmployee.employeeSalary) - val hasError = listOf(validateEmployeeName, validateEmployeePhone, validateEmployeePosition, validateEmployeeSalary).any { !it.successful } + val hasError = listOf( + validateEmployeeName, + validateEmployeePhone, + validateEmployeePosition, + validateEmployeeSalary + ).any { !it.successful } if (!hasError) { withContext(ioDispatcher) { - val result = employeeDao.updateEmployee(newEmployee) + val result = employeeDao.updateEmployee(newEmployee.toEntity()) Resource.Success(result > 0) } - }else { + } else { Resource.Error("Unable to validate employee") } } @@ -91,18 +110,25 @@ class EmployeeRepositoryImpl( return try { withContext(ioDispatcher) { val validateEmployeeName = validateEmployeeName(newEmployee.employeeName) - val validateEmployeePhone = validateEmployeePhone(newEmployee.employeePhone, newEmployee.employeeId) - val validateEmployeePosition = validateEmployeePosition(newEmployee.employeePosition) + val validateEmployeePhone = + validateEmployeePhone(newEmployee.employeePhone, newEmployee.employeeId) + val validateEmployeePosition = + validateEmployeePosition(newEmployee.employeePosition) val validateEmployeeSalary = validateEmployeeSalary(newEmployee.employeeSalary) - val hasError = listOf(validateEmployeeName, validateEmployeePhone, validateEmployeePosition, validateEmployeeSalary).any { !it.successful } + val hasError = listOf( + validateEmployeeName, + validateEmployeePhone, + validateEmployeePosition, + validateEmployeeSalary + ).any { !it.successful } if (!hasError) { withContext(ioDispatcher) { - val result = employeeDao.upsertEmployee(newEmployee) + val result = employeeDao.upsertEmployee(newEmployee.toEntity()) Resource.Success(result > 0) } - }else { + } else { Resource.Error("Unable to validate employee") } } @@ -118,7 +144,7 @@ class EmployeeRepositoryImpl( Resource.Success(result > 0) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error("Unable to delete Employee") } } @@ -130,27 +156,27 @@ class EmployeeRepositoryImpl( Resource.Success(result > 0) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error("Unable to delete Employee") } } override suspend fun validateEmployeeName(name: String, employeeId: Int?): ValidationResult { - if(name.isEmpty()){ + if (name.isEmpty()) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_NAME_EMPTY_ERROR, ) } - if(name.length < 4){ + if (name.length < 4) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_NAME_LENGTH_ERROR, ) } - if(name.any { it.isDigit() }){ + if (name.any { it.isDigit() }) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_NAME_DIGIT_ERROR @@ -161,7 +187,7 @@ class EmployeeRepositoryImpl( employeeDao.findEmployeeByName(name, employeeId) != null } - if(serverResult){ + if (serverResult) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_NAME_ALREADY_EXIST_ERROR, @@ -175,23 +201,23 @@ class EmployeeRepositoryImpl( override suspend fun validateEmployeePhone( phone: String, - employeeId: Int? + employeeId: Int?, ): ValidationResult { - if(phone.isEmpty()){ + if (phone.isEmpty()) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_PHONE_EMPTY_ERROR ) } - if(phone.length != 10){ + if (phone.length != 10) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_PHONE_LENGTH_ERROR ) } - if(phone.any { it.isLetter() }){ + if (phone.any { it.isLetter() }) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_PHONE_LETTER_ERROR @@ -202,7 +228,7 @@ class EmployeeRepositoryImpl( employeeDao.findEmployeeByPhone(phone, employeeId) != null } - if(serverResult){ + if (serverResult) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_PHONE_ALREADY_EXIST_ERROR @@ -215,7 +241,7 @@ class EmployeeRepositoryImpl( } override fun validateEmployeePosition(position: String): ValidationResult { - if (position.isEmpty()){ + if (position.isEmpty()) { return ValidationResult(false, EmployeeTestTags.EMPLOYEE_POSITION_EMPTY_ERROR) } @@ -223,21 +249,21 @@ class EmployeeRepositoryImpl( } override fun validateEmployeeSalary(salary: String): ValidationResult { - if (salary.isEmpty()){ + if (salary.isEmpty()) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_SALARY_EMPTY_ERROR ) } - if(salary.length != 5){ + if (salary.length != 5) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_SALARY_LENGTH_ERROR ) } - if(salary.any { it.isLetter() }){ + if (salary.any { it.isLetter() }) { return ValidationResult( successful = false, errorMessage = EmployeeTestTags.EMPLOYEE_SALARY_LETTER_ERROR diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/data/repository/ExpenseRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/ExpenseRepositoryImpl.kt similarity index 69% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/data/repository/ExpenseRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/ExpenseRepositoryImpl.kt index 6b28cd06..bce96518 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/data/repository/ExpenseRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/ExpenseRepositoryImpl.kt @@ -1,20 +1,21 @@ -package com.niyaj.poposroom.features.expenses.data.repository - -import androidx.sqlite.db.SimpleSQLiteQuery -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult -import com.niyaj.poposroom.features.expenses.data.dao.ExpenseDao -import com.niyaj.poposroom.features.expenses.domain.model.Expense -import com.niyaj.poposroom.features.expenses.domain.model.searchExpense -import com.niyaj.poposroom.features.expenses.domain.repository.ExpenseRepository -import com.niyaj.poposroom.features.expenses.domain.repository.ExpenseValidationRepository -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_DATE_EMPTY_ERROR -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_NAME_EMPTY_ERROR -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_NAME_LENGTH_ERROR -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_PRICE_EMPTY_ERROR -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_PRICE_LESS_THAN_TEN_ERROR +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.ExpenseRepository +import com.niyaj.data.repository.ExpenseValidationRepository +import com.niyaj.database.dao.ExpenseDao +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Expense +import com.niyaj.model.searchExpense +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_DATE_EMPTY_ERROR +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_NAME_EMPTY_ERROR +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_NAME_LENGTH_ERROR +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_PRICE_EMPTY_ERROR +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_PRICE_LESS_THAN_TEN_ERROR import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -24,13 +25,17 @@ import kotlinx.coroutines.withContext class ExpenseRepositoryImpl( private val expenseDao: ExpenseDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : ExpenseRepository, ExpenseValidationRepository { @OptIn(ExperimentalCoroutinesApi::class) override suspend fun getAllExpense(searchText: String, givenDate: String): Flow> { return withContext(ioDispatcher) { - expenseDao.getAllExpense(givenDate).mapLatest { it.searchExpense(searchText) } + expenseDao.getAllExpense(givenDate).mapLatest { it -> + it.map { + it.asExternalModel() + }.searchExpense(searchText) + } } } @@ -43,28 +48,18 @@ class ExpenseRepositoryImpl( } } - override suspend fun getAllPagingExpenses(searchText: String, limit: Int, offset: Int): List { - val query = if (searchText.isNotEmpty()) { - SimpleSQLiteQuery( - "SELECT * FROM expense " + - "WHERE expenseName LIKE ? " + - "OR expenseAmount LIKE ? " + - "ORDER BY expenseDate DESC LIMIT ? OFFSET ?", - arrayOf(searchText, searchText, limit, offset) - ) - }else { - SimpleSQLiteQuery( - "SELECT * FROM expense ORDER BY expenseDate DESC LIMIT ? OFFSET ?", - arrayOf(limit, offset) - ) - } - - return withContext(ioDispatcher) { - expenseDao.getAllPagingExpenses(query) - } + override suspend fun getAllPagingExpenses( + searchText: String, + limit: Int, + offset: Int, + ): List { + TODO("Not yet implemented") } - override suspend fun findExpenseByNameAndDate(expenseName: String, expenseDate: String): Boolean { + override suspend fun findExpenseByNameAndDate( + expenseName: String, + expenseDate: String, + ): Boolean { return withContext(ioDispatcher) { expenseDao.findExpenseByNameAndDate(expenseName, expenseDate) != null } @@ -73,9 +68,9 @@ class ExpenseRepositoryImpl( override suspend fun getExpenseById(expenseId: Int): Resource { return try { withContext(ioDispatcher) { - Resource.Success(expenseDao.getExpenseById(expenseId)) + Resource.Success(expenseDao.getExpenseById(expenseId)?.asExternalModel()) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error("Unable to get expense details") } } @@ -93,11 +88,11 @@ class ExpenseRepositoryImpl( if (!hasError) { val result = withContext(ioDispatcher) { - expenseDao.insertOrIgnoreExpense(newExpense) + expenseDao.insertOrIgnoreExpense(newExpense.toEntity()) } Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to validate expenses") } } @@ -119,11 +114,11 @@ class ExpenseRepositoryImpl( if (!hasError) { val result = withContext(ioDispatcher) { - expenseDao.updateExpense(newExpense) + expenseDao.updateExpense(newExpense.toEntity()) } Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to validate expenses") } } @@ -145,11 +140,11 @@ class ExpenseRepositoryImpl( if (!hasError) { val result = withContext(ioDispatcher) { - expenseDao.upsertExpense(newExpense) + expenseDao.upsertExpense(newExpense.toEntity()) } Resource.Success(result > 0) - }else { + } else { Resource.Error("Unable to validate expenses") } } @@ -165,7 +160,7 @@ class ExpenseRepositoryImpl( } Resource.Success(result > 0) - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error("Unable to delete expense") } } @@ -177,13 +172,13 @@ class ExpenseRepositoryImpl( } Resource.Success(result > 0) - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error("Unable to delete expenses") } } override fun validateExpenseName(expenseName: String): ValidationResult { - if (expenseName.isEmpty()){ + if (expenseName.isEmpty()) { return ValidationResult( successful = false, errorMessage = EXPENSE_NAME_EMPTY_ERROR @@ -201,7 +196,7 @@ class ExpenseRepositoryImpl( } override fun validateExpenseDate(expenseDate: String): ValidationResult { - if (expenseDate.isEmpty()){ + if (expenseDate.isEmpty()) { return ValidationResult( successful = false, errorMessage = EXPENSE_DATE_EMPTY_ERROR @@ -212,7 +207,7 @@ class ExpenseRepositoryImpl( } override fun validateExpenseAmount(expenseAmount: String): ValidationResult { - if (expenseAmount.isEmpty()){ + if (expenseAmount.isEmpty()) { return ValidationResult( successful = false, errorMessage = EXPENSE_PRICE_EMPTY_ERROR diff --git a/app/src/main/java/com/niyaj/poposroom/features/main_feed/data/repository/MainFeedRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/MainFeedRepositoryImpl.kt similarity index 68% rename from app/src/main/java/com/niyaj/poposroom/features/main_feed/data/repository/MainFeedRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/MainFeedRepositoryImpl.kt index 1323adfb..5525de94 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/main_feed/data/repository/MainFeedRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/MainFeedRepositoryImpl.kt @@ -1,15 +1,16 @@ -package com.niyaj.poposroom.features.main_feed.data.repository +package com.niyaj.data.data.repository -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.main_feed.data.dao.MainFeedDao -import com.niyaj.poposroom.features.main_feed.domain.model.ProductWithFlowQuantity -import com.niyaj.poposroom.features.main_feed.domain.model.filterByCategory -import com.niyaj.poposroom.features.main_feed.domain.model.filterBySearch -import com.niyaj.poposroom.features.main_feed.domain.repository.MainFeedRepository -import com.niyaj.poposroom.features.product.domain.model.Product -import com.niyaj.poposroom.features.selected.domain.model.Selected +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.repository.MainFeedRepository +import com.niyaj.database.dao.MainFeedDao +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Category +import com.niyaj.model.Product +import com.niyaj.model.ProductWithFlowQuantity +import com.niyaj.model.Selected +import com.niyaj.model.filterByCategory +import com.niyaj.model.filterBySearch import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow @@ -17,21 +18,26 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.withContext class MainFeedRepositoryImpl( private val mainFeedDao: MainFeedDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : MainFeedRepository { override fun getAllCategory(): Flow> { - return mainFeedDao.getAllCategories() + return mainFeedDao.getAllCategories().mapLatest { list -> + list.map { + it.asExternalModel() + } + } } override suspend fun getAllProduct( searchText: String, - selectedCategory: Int + selectedCategory: Int, ): Flow> { return channelFlow { withContext(ioDispatcher) { @@ -39,7 +45,9 @@ class MainFeedRepositoryImpl( val products = mainFeedDao.getAllProducts() selectedCartOrder.combine(products) { order, list -> - mapProductToProductWithQuantity(order?.orderId, list) + mapProductToProductWithQuantity( + order?.orderId, + list.map { it.asExternalModel() }) }.collectLatest { result -> val filteredProducts = result .filter { @@ -55,7 +63,7 @@ class MainFeedRepositoryImpl( } override fun getSelectedOrder(): Flow { - return mainFeedDao.getSelectedOrder() + return mainFeedDao.getSelectedOrder().mapLatest { it?.asExternalModel() } } private suspend fun getQuantity(orderId: Int?, productId: Int): Flow { @@ -68,7 +76,7 @@ class MainFeedRepositoryImpl( private suspend fun mapProductToProductWithQuantity( selectedCartOrder: Int? = null, - products: List + products: List, ): List { val data = products.map { product -> ProductWithFlowQuantity( diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/data/repository/OrderRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/OrderRepositoryImpl.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/order/data/repository/OrderRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/OrderRepositoryImpl.kt index 41f06e51..dcffac3e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/data/repository/OrderRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/OrderRepositoryImpl.kt @@ -1,26 +1,30 @@ -package com.niyaj.poposroom.features.order.data.repository - -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.cart.domain.model.CartProductItem -import com.niyaj.poposroom.features.cart.domain.model.OrderPrice -import com.niyaj.poposroom.features.cart.domain.model.OrderWithCart -import com.niyaj.poposroom.features.cart_order.data.dao.CartOrderDao -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrder -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.calculateEndDate -import com.niyaj.poposroom.features.common.utils.calculateStartDate -import com.niyaj.poposroom.features.common.utils.getEndDateLong -import com.niyaj.poposroom.features.common.utils.getStartDateLong -import com.niyaj.poposroom.features.customer.domain.model.Customer -import com.niyaj.poposroom.features.order.data.dao.OrderDao -import com.niyaj.poposroom.features.order.domain.model.Order -import com.niyaj.poposroom.features.order.domain.model.OrderDetails -import com.niyaj.poposroom.features.order.domain.model.searchOrder -import com.niyaj.poposroom.features.order.domain.repository.OrderRepository +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.calculateEndDate +import com.niyaj.common.utils.calculateStartDate +import com.niyaj.common.utils.getEndDateLong +import com.niyaj.common.utils.getStartDateLong +import com.niyaj.data.repository.OrderRepository +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.OrderDao +import com.niyaj.database.dao.SelectedDao +import com.niyaj.database.model.OrderWithCartDto +import com.niyaj.database.model.SelectedEntity +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Address +import com.niyaj.model.CartOrder +import com.niyaj.model.CartProductItem +import com.niyaj.model.Charges +import com.niyaj.model.Customer +import com.niyaj.model.Order +import com.niyaj.model.OrderDetails +import com.niyaj.model.OrderPrice +import com.niyaj.model.OrderType +import com.niyaj.model.SELECTED_ID +import com.niyaj.model.searchOrder import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async @@ -32,8 +36,9 @@ import kotlinx.coroutines.withContext class OrderRepositoryImpl( private val orderDao: OrderDao, private val cartOrderDao: CartOrderDao, + private val selectedDao: SelectedDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : OrderRepository { @OptIn(ExperimentalCoroutinesApi::class) @@ -45,7 +50,7 @@ class OrderRepositoryImpl( val endDate = if (date.isNotEmpty()) { calculateEndDate(date) - }else getEndDateLong + } else getEndDateLong orderDao.getAllOrders(startDate, endDate).mapLatest { list -> mapCartItemToOrder(list) @@ -57,7 +62,9 @@ class OrderRepositoryImpl( override suspend fun getAllCharges(): Flow> { return withContext(ioDispatcher) { - orderDao.getAllCharges() + orderDao.getAllCharges().mapLatest { it -> + it.map { it.asExternalModel() } + } } } @@ -75,9 +82,13 @@ class OrderRepositoryImpl( withContext(ioDispatcher) { val result = cartOrderDao.deleteCartOrder(orderId) + async { + updateOrDeleteSelectedOrder() + }.await() + Resource.Success(result > 0) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message) } } @@ -87,14 +98,18 @@ class OrderRepositoryImpl( withContext(ioDispatcher) { val result = cartOrderDao.markAsProcessing(orderId) + async { + updateOrDeleteSelectedOrder() + }.await() + Resource.Success(result > 0) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message) } } - private suspend fun mapCartItemToOrder(cartOrders: List): List { + private suspend fun mapCartItemToOrder(cartOrders: List): List { return withContext(ioDispatcher) { cartOrders.map { order -> var totalPrice = 0 @@ -133,16 +148,13 @@ class OrderRepositoryImpl( } @OptIn(ExperimentalCoroutinesApi::class) - private suspend fun mapCartItemToOrderDetails(order: OrderWithCart): OrderDetails { + private suspend fun mapCartItemToOrderDetails(order: OrderWithCartDto): OrderDetails { return withContext(ioDispatcher) { - var totalPrice = 0 - val discountPrice = 0 - val addOnItems = async(ioDispatcher) { cartOrderDao.getCartAddOnItemsId(order.cartOrder.orderId).mapLatest { list -> list.map { - withContext(ioDispatcher){ - cartOrderDao.getAddOnItemById(it) + withContext(ioDispatcher) { + cartOrderDao.getAddOnItemById(it).asExternalModel() } } } @@ -151,7 +163,7 @@ class OrderRepositoryImpl( val charges = async(ioDispatcher) { cartOrderDao.getCartChargesId(order.cartOrder.orderId).mapLatest { list -> list.map { - cartOrderDao.getChargesById(it) + cartOrderDao.getChargesById(it).asExternalModel() } } } @@ -159,9 +171,6 @@ class OrderRepositoryImpl( val cartProducts = async(ioDispatcher) { order.cartItems.map { cartItem -> val product = orderDao.getProductById(cartItem.productId) - - totalPrice += product.productPrice.times(cartItem.quantity) - CartProductItem( productId = product.productId, productName = product.productName, @@ -173,13 +182,13 @@ class OrderRepositoryImpl( val address = async(ioDispatcher) { if (order.cartOrder.orderType != OrderType.DineIn) { - orderDao.getAddressById(order.cartOrder.addressId) + orderDao.getAddressById(order.cartOrder.addressId).asExternalModel() } else Address() } val customer = async(ioDispatcher) { if (order.cartOrder.orderType != OrderType.DineIn) { - orderDao.getCustomerById(order.cartOrder.customerId) + orderDao.getCustomerById(order.cartOrder.customerId).asExternalModel() } else Customer() } @@ -241,7 +250,7 @@ class OrderRepositoryImpl( async(ioDispatcher) { if (included) { - cartOrderDao.getAllChargesPrice().forEach { it -> + cartOrderDao.getAllChargesPrice().forEach { if (it.isApplicable && orderType == OrderType.DineOut) { totalPrice += it.chargesPrice } @@ -253,7 +262,10 @@ class OrderRepositoryImpl( val data = cartOrderDao.getCartProductsByOrderId(orderId) data.cartItems.forEach { - val result = cartOrderDao.getProductPriceAndQuantity(data.cartOrder.orderId, it.productId) + val result = cartOrderDao.getProductPriceAndQuantity( + data.cartOrder.orderId, + it.productId + ) totalPrice += result.productPrice.times(it.quantity) } @@ -265,4 +277,20 @@ class OrderRepositoryImpl( return OrderPrice(totalPrice, discountPrice) } + + private suspend fun updateOrDeleteSelectedOrder() { + withContext(ioDispatcher) { + val lastId = cartOrderDao.getLastProcessingId() + + lastId?.let { + selectedDao.insertOrUpdateSelectedOrder( + SelectedEntity( + selectedId = SELECTED_ID, + orderId = it + ) + ) + } ?: selectedDao.deleteSelectedOrder(SELECTED_ID) + } + } + } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/data/repository/PaymentRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/PaymentRepositoryImpl.kt similarity index 79% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/data/repository/PaymentRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/PaymentRepositoryImpl.kt index 1813a12d..609f7072 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/data/repository/PaymentRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/PaymentRepositoryImpl.kt @@ -1,24 +1,25 @@ -package com.niyaj.poposroom.features.employee_payment.data.repository - -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee.domain.utils.PaymentMode -import com.niyaj.poposroom.features.employee.domain.utils.PaymentType +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.PaymentRepository +import com.niyaj.data.repository.validation.PaymentValidationRepository +import com.niyaj.database.model.EmployeeWithPaymentCrossRef +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.CalculatedSalary +import com.niyaj.model.Employee +import com.niyaj.model.EmployeeWithPayments +import com.niyaj.model.Payment +import com.niyaj.model.PaymentMode +import com.niyaj.model.PaymentType +import com.niyaj.model.SalaryCalculableDate +import com.niyaj.model.SalaryCalculation +import com.niyaj.model.searchPayment import com.niyaj.poposroom.features.employee_payment.data.dao.PaymentDao -import com.niyaj.poposroom.features.employee_payment.domain.model.CalculatedSalary -import com.niyaj.poposroom.features.employee_payment.domain.model.EmployeeWithPayment -import com.niyaj.poposroom.features.employee_payment.domain.model.EmployeeWithPaymentCrossRef -import com.niyaj.poposroom.features.employee_payment.domain.model.Payment -import com.niyaj.poposroom.features.employee_payment.domain.model.SalaryCalculableDate -import com.niyaj.poposroom.features.employee_payment.domain.model.SalaryCalculation -import com.niyaj.poposroom.features.employee_payment.domain.model.searchEmployeeWithPayments -import com.niyaj.poposroom.features.employee_payment.domain.model.searchPayment -import com.niyaj.poposroom.features.employee_payment.domain.repository.PaymentRepository -import com.niyaj.poposroom.features.employee_payment.domain.repository.PaymentValidationRepository -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags +import com.niyaj.data.utils.PaymentScreenTags import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -30,37 +31,49 @@ import kotlinx.coroutines.withContext class PaymentRepositoryImpl( private val paymentDao: PaymentDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : PaymentRepository, PaymentValidationRepository { - override fun getAllEmployee(): Flow> = paymentDao.getAllEmployee() + override fun getAllEmployee(): Flow> { + return paymentDao.getAllEmployee().mapLatest { list -> + list.map { + it.asExternalModel() + } + } + } override suspend fun getEmployeeById(employeeId: Int): Employee? { return withContext(ioDispatcher) { - paymentDao.getEmployeeById(employeeId) + paymentDao.getEmployeeById(employeeId)?.asExternalModel() } } - override suspend fun getAllEmployeePayments(searchText: String): Flow> { + override suspend fun getAllEmployeePayments(searchText: String): Flow> { return withContext(ioDispatcher) { - paymentDao.getAllEmployeePayment().mapLatest { - it.searchEmployeeWithPayments(searchText) + paymentDao.getAllEmployeePayment().mapLatest { list -> + list.map { + it.asExternalModel() + } } } } override suspend fun getAllPayment(searchText: String): Flow> { return withContext(ioDispatcher) { - paymentDao.getAllPayment().mapLatest { it.searchPayment(searchText) } + paymentDao.getAllPayment().mapLatest { it -> + it.map { + it.asExternalModel() + }.searchPayment(searchText) + } } } - override suspend fun getPaymentById(paymentId: Int): Resource { + override suspend fun getPaymentById(paymentId: Int): Resource { return try { withContext(ioDispatcher) { - Resource.Success(paymentDao.getPaymentById(paymentId)) + Resource.Success(paymentDao.getPaymentById(paymentId)?.asExternalModel()) } - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message) } } @@ -89,7 +102,7 @@ class PaymentRepositoryImpl( if (!hasError) { withContext(ioDispatcher) { val result = withContext(ioDispatcher) { - paymentDao.insertOrIgnorePayment(newPayment) + paymentDao.insertOrIgnorePayment(newPayment.toEntity()) } if (result > 0) { @@ -100,10 +113,10 @@ class PaymentRepositoryImpl( Resource.Success(result > 0) } - }else { + } else { Resource.Error("Unable to validate employee payment") } - }catch (e: Exception){ + } catch (e: Exception) { Resource.Error("Unable to add employee payment") } } @@ -132,15 +145,15 @@ class PaymentRepositoryImpl( if (!hasError) { withContext(ioDispatcher) { val result = withContext(ioDispatcher) { - paymentDao.updatePayment(newPayment) + paymentDao.updatePayment(newPayment.toEntity()) } Resource.Success(result > 0) } - }else { + } else { Resource.Error("Unable to validate employee payment") } - }catch (e: Exception){ + } catch (e: Exception) { Resource.Error("Unable to update employee payment") } } @@ -169,7 +182,7 @@ class PaymentRepositoryImpl( if (!hasError) { withContext(ioDispatcher) { val result = withContext(ioDispatcher) { - paymentDao.upsertPayment(newPayment) + paymentDao.upsertPayment(newPayment.toEntity()) } if (result > 0) { @@ -180,10 +193,10 @@ class PaymentRepositoryImpl( Resource.Success(result > 0) } - }else { + } else { Resource.Error("Unable to validate employee payment") } - }catch (e: Exception){ + } catch (e: Exception) { Resource.Error("Unable to add or update employee payment") } } @@ -195,7 +208,7 @@ class PaymentRepositoryImpl( } Resource.Success(result > 0) - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error("Unable to delete employee payment") } } @@ -207,28 +220,28 @@ class PaymentRepositoryImpl( } Resource.Success(result > 0) - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error("Unable to delete employee payments") } } override suspend fun getPaymentByEmployeeId( employeeId: Int, - selectedDate: Pair + selectedDate: Pair, ): CalculatedSalary? { return null } override suspend fun getEmployeePayment( - employeeId: Int + employeeId: Int, ): Flow> { - return flow { } + return flow { } } override suspend fun getPaymentCalculableDate( - employeeId: Int + employeeId: Int, ): Flow> { - return flow { } + return flow { } } override fun validateEmployee(employeeId: Int): ValidationResult { @@ -297,7 +310,7 @@ class PaymentRepositoryImpl( override fun validatePaymentNote(paymentNote: String, isRequired: Boolean): ValidationResult { if (isRequired) { - if (paymentNote.isEmpty()){ + if (paymentNote.isEmpty()) { return ValidationResult( successful = false, errorMessage = PaymentScreenTags.PAYMENT_NOTE_EMPTY diff --git a/core/data/src/main/java/com/niyaj/data/data/repository/PrintRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/PrintRepositoryImpl.kt new file mode 100644 index 00000000..ab9ada8f --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/data/repository/PrintRepositoryImpl.kt @@ -0,0 +1,26 @@ +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.repository.PrintRepository +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.PrintDao +import com.niyaj.model.OrderDetails +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.Flow + +class PrintRepositoryImpl( + private val printDao: PrintDao, + private val cartOrderDao: CartOrderDao, + @Dispatcher(PoposDispatchers.IO) + private val ioDispatcher: CoroutineDispatcher +) : PrintRepository { + + override suspend fun getOrderDetail(orderId: Int): OrderDetails { + TODO("Not yet implemented") + } + + override suspend fun getOrderDetails(orderIds: List): Flow> { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/data/repository/PrinterRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/PrinterRepositoryImpl.kt new file mode 100644 index 00000000..6474314b --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/data/repository/PrinterRepositoryImpl.kt @@ -0,0 +1,44 @@ +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.PrinterRepository +import com.niyaj.database.dao.PrinterDao +import com.niyaj.database.model.PrinterEntity +import com.niyaj.database.model.toExternalModel +import com.niyaj.model.Printer +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.withContext + +class PrinterRepositoryImpl( + private val printerDao: PrinterDao, + @Dispatcher(PoposDispatchers.IO) + private val ioDispatcher: CoroutineDispatcher, +) : PrinterRepository { + + override suspend fun getPrinter(printerId: String): Printer { + return withContext(ioDispatcher) { + val data = printerDao.printerInfo(printerId) + + data?.toExternalModel() ?: PrinterEntity().toExternalModel() + } + } + + override suspend fun getPrinterInfo(printerId: String): Flow { + return flow { } + } + + override suspend fun addOrUpdatePrinterInfo(printer: Printer): Resource { + return try { + val result = printerDao.insertOrUpdatePrinterInfo(printer.toEntity()) + + Resource.Success(result > 0) + } catch (e: Exception) { + Resource.Error(e.message) + } + } +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/data/repository/PrinterValidationRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/PrinterValidationRepositoryImpl.kt new file mode 100644 index 00000000..9b483620 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/data/repository/PrinterValidationRepositoryImpl.kt @@ -0,0 +1,123 @@ +package com.niyaj.data.data.repository + +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.repository.validation.PrinterValidationRepository +import com.niyaj.data.utils.PrinterInfoTestTags.ADDRESS_REPORT_LENGTH_ERROR +import com.niyaj.data.utils.PrinterInfoTestTags.ADDRESS_REPORT_LIMIT_IS_REQUIRED +import com.niyaj.data.utils.PrinterInfoTestTags.CUSTOM_REPORT_LENGTH_ERROR +import com.niyaj.data.utils.PrinterInfoTestTags.CUSTOM_REPORT_LIMIT_IS_REQUIRED +import com.niyaj.data.utils.PrinterInfoTestTags.DPI_IS_REQUIRED +import com.niyaj.data.utils.PrinterInfoTestTags.NBR_LINES_IS_REQUIRED +import com.niyaj.data.utils.PrinterInfoTestTags.PRODUCT_NAME_LENGTH_IS_ERROR +import com.niyaj.data.utils.PrinterInfoTestTags.PRODUCT_NAME_LENGTH_IS_REQUIRED +import com.niyaj.data.utils.PrinterInfoTestTags.PRODUCT_REPORT_LENGTH_ERROR +import com.niyaj.data.utils.PrinterInfoTestTags.PRODUCT_REPORT_LIMIT_IS_REQUIRED +import com.niyaj.data.utils.PrinterInfoTestTags.WIDTH_IS_REQUIRED + +class PrinterValidationRepositoryImpl : PrinterValidationRepository { + + override fun validatePrinterDpi(dpi: Int): ValidationResult { + if (dpi <= 0) { + return ValidationResult( + successful = false, + errorMessage = DPI_IS_REQUIRED + ) + } + + return ValidationResult(true) + } + + override fun validatePrinterWidth(width: Float): ValidationResult { + if (width <= 0f) { + return ValidationResult( + successful = false, + errorMessage = WIDTH_IS_REQUIRED + ) + } + + return ValidationResult(true) + } + + override fun validatePrinterNbrLines(lines: Int): ValidationResult { + if (lines <= 0) { + return ValidationResult( + successful = false, + errorMessage = NBR_LINES_IS_REQUIRED + ) + } + + return ValidationResult(true) + } + + override fun validateProductNameLength(length: Int): ValidationResult { + if (length <= 0) { + return ValidationResult( + successful = false, + errorMessage = PRODUCT_NAME_LENGTH_IS_REQUIRED + ) + } + + if (length <= 10) { + return ValidationResult( + successful = false, + errorMessage = PRODUCT_NAME_LENGTH_IS_ERROR + ) + } + + return ValidationResult(true) + } + + override fun validateProductReportLimit(limit: Int): ValidationResult { + if (limit <= 0) { + return ValidationResult( + successful = false, + errorMessage = PRODUCT_REPORT_LIMIT_IS_REQUIRED + ) + } + + if (limit < 20) { + return ValidationResult( + successful = false, + errorMessage = PRODUCT_REPORT_LENGTH_ERROR + ) + } + + return ValidationResult(true) + } + + override fun validateAddressReportLimit(limit: Int): ValidationResult { + if (limit <= 0) { + return ValidationResult( + successful = false, + errorMessage = ADDRESS_REPORT_LIMIT_IS_REQUIRED + ) + } + + if (limit < 10) { + return ValidationResult( + successful = false, + errorMessage = ADDRESS_REPORT_LENGTH_ERROR + ) + } + + return ValidationResult(true) + } + + override fun validateCustomerReportLimit(limit: Int): ValidationResult { + if (limit <= 0) { + return ValidationResult( + successful = false, + errorMessage = CUSTOM_REPORT_LIMIT_IS_REQUIRED + ) + } + + if (limit < 10) { + return ValidationResult( + successful = false, + errorMessage = CUSTOM_REPORT_LENGTH_ERROR + ) + } + + return ValidationResult(true) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/data/repository/ProductRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/ProductRepositoryImpl.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/product/data/repository/ProductRepositoryImpl.kt rename to core/data/src/main/java/com/niyaj/data/data/repository/ProductRepositoryImpl.kt index 0a804dee..273e6dd9 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/data/repository/ProductRepositoryImpl.kt +++ b/core/data/src/main/java/com/niyaj/data/data/repository/ProductRepositoryImpl.kt @@ -1,24 +1,26 @@ -package com.niyaj.poposroom.features.product.data.repository - -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.category.domain.model.filterCategory -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.ValidationResult -import com.niyaj.poposroom.features.product.data.dao.ProductDao -import com.niyaj.poposroom.features.product.domain.model.CategoryWithProduct -import com.niyaj.poposroom.features.product.domain.model.CategoryWithProductCrossRef -import com.niyaj.poposroom.features.product.domain.model.Product -import com.niyaj.poposroom.features.product.domain.model.filterProducts -import com.niyaj.poposroom.features.product.domain.repository.ProductRepository -import com.niyaj.poposroom.features.product.domain.repository.ProductValidationRepository -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_CATEGORY_EMPTY_ERROR -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_NAME_ALREADY_EXIST_ERROR -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_NAME_EMPTY_ERROR -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_NAME_LENGTH_ERROR -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_PRICE_EMPTY_ERROR -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_PRICE_LENGTH_ERROR +package com.niyaj.data.data.repository + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.ProductRepository +import com.niyaj.data.repository.validation.ProductValidationRepository +import com.niyaj.database.dao.ProductDao +import com.niyaj.database.model.CategoryWithProductCrossRef +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Category +import com.niyaj.model.CategoryWithProducts +import com.niyaj.model.Product +import com.niyaj.model.filterCategory +import com.niyaj.model.filterProducts +import com.niyaj.data.utils.ProductTestTags.PRODUCT_CATEGORY_EMPTY_ERROR +import com.niyaj.data.utils.ProductTestTags.PRODUCT_NAME_ALREADY_EXIST_ERROR +import com.niyaj.data.utils.ProductTestTags.PRODUCT_NAME_EMPTY_ERROR +import com.niyaj.data.utils.ProductTestTags.PRODUCT_NAME_LENGTH_ERROR +import com.niyaj.data.utils.ProductTestTags.PRODUCT_PRICE_EMPTY_ERROR +import com.niyaj.data.utils.ProductTestTags.PRODUCT_PRICE_LENGTH_ERROR import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow @@ -28,34 +30,44 @@ import kotlinx.coroutines.withContext class ProductRepositoryImpl( private val productDao: ProductDao, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : ProductRepository, ProductValidationRepository { override fun getAllCategory(): Flow> { - return productDao.getAllCategory() + return productDao.getAllCategory().mapLatest { list -> + list.map { + it.asExternalModel() + } + } } override suspend fun getCategoryById(categoryId: Int): Category? { return withContext(ioDispatcher) { - productDao.getCategoryById(categoryId) + productDao.getCategoryById(categoryId)?.asExternalModel() } } @OptIn(ExperimentalCoroutinesApi::class) - override suspend fun getAllCategoryProducts(searchText: String): Flow> { + override suspend fun getAllCategoryProducts(searchText: String): Flow> { return withContext(ioDispatcher) { productDao.getAllCategoryProduct().mapLatest { list -> - list.filter { - it.category.filterCategory(searchText) - } + list.map { it.asExternalModel() } + .filter { + it.category.filterCategory(searchText) + } } } } @OptIn(ExperimentalCoroutinesApi::class) - override suspend fun getAllProduct(searchText: String, selectedCategory: Int): Flow> { + override suspend fun getAllProduct( + searchText: String, + selectedCategory: Int, + ): Flow> { return withContext(ioDispatcher) { - productDao.getAllProduct().mapLatest { list -> + productDao.getAllProduct().mapLatest { entityList -> + entityList.map { it.asExternalModel() } + }.mapLatest { list -> if (selectedCategory != 0) { list.filter { it.categoryId == selectedCategory } } else list @@ -68,7 +80,7 @@ class ProductRepositoryImpl( override suspend fun getProductById(productId: Int): Resource { return withContext(ioDispatcher) { try { - Resource.Success(productDao.getProductById(productId)) + Resource.Success(productDao.getProductById(productId)?.asExternalModel()) } catch (e: Exception) { Resource.Error(e.message ?: "Product not found") } @@ -78,7 +90,7 @@ class ProductRepositoryImpl( override suspend fun addOrIgnoreProduct(newProduct: Product): Resource { return try { withContext(ioDispatcher) { - val validateCategory = validateCategoryName(newProduct.categoryId) + val validateCategory = validateCategoryId(newProduct.categoryId) val valProduct = validateProductName(newProduct.productName) val validateProductPrice = validateProductPrice(newProduct.productPrice) @@ -96,7 +108,7 @@ class ProductRepositoryImpl( if (category != null) { val result = withContext(ioDispatcher) { - productDao.insertOrIgnoreProduct(newProduct) + productDao.insertOrIgnoreProduct(newProduct.toEntity()) } Resource.Success(result > 0) @@ -116,7 +128,7 @@ class ProductRepositoryImpl( override suspend fun updateProduct(newProduct: Product): Resource { return try { withContext(ioDispatcher) { - val validateCategory = validateCategoryName(newProduct.categoryId) + val validateCategory = validateCategoryId(newProduct.categoryId) val valProduct = validateProductName(newProduct.productName, newProduct.productId) val validateProductPrice = validateProductPrice(newProduct.productPrice) @@ -134,7 +146,7 @@ class ProductRepositoryImpl( if (category != null) { val result = withContext(ioDispatcher) { - productDao.updateProduct(newProduct) + productDao.updateProduct(newProduct.toEntity()) } Resource.Success(result > 0) @@ -154,7 +166,7 @@ class ProductRepositoryImpl( override suspend fun upsertProduct(newProduct: Product): Resource { return try { withContext(ioDispatcher) { - val validateCategory = validateCategoryName(newProduct.categoryId) + val validateCategory = validateCategoryId(newProduct.categoryId) val valProduct = validateProductName(newProduct.productName, newProduct.productId) val validateProductPrice = validateProductPrice(newProduct.productPrice) @@ -172,13 +184,16 @@ class ProductRepositoryImpl( if (category != null) { val result = withContext(ioDispatcher) { - productDao.upsertProduct(newProduct) + productDao.upsertProduct(newProduct.toEntity()) } if (result > 0) { withContext(ioDispatcher) { productDao.upsertCategoryWithProductCrossReference( - CategoryWithProductCrossRef(newProduct.categoryId, result.toInt()) + CategoryWithProductCrossRef( + newProduct.categoryId, + result.toInt() + ) ) } } @@ -204,7 +219,7 @@ class ProductRepositoryImpl( } Resource.Success(result > 0) - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message ?: "Unable to delete product") } } @@ -216,12 +231,12 @@ class ProductRepositoryImpl( } Resource.Success(result > 0) - }catch (e: Exception) { + } catch (e: Exception) { Resource.Error(e.message ?: "Unable to delete products") } } - override fun validateCategoryName(categoryId: Int): ValidationResult { + override fun validateCategoryId(categoryId: Int): ValidationResult { if (categoryId == 0) { return ValidationResult( successful = false, @@ -236,7 +251,7 @@ class ProductRepositoryImpl( override suspend fun validateProductName( productName: String, - productId: Int? + productId: Int?, ): ValidationResult { if (productName.isEmpty()) { return ValidationResult( diff --git a/core/data/src/main/java/com/niyaj/data/data/repository/ProfileRepositoryImpl.kt b/core/data/src/main/java/com/niyaj/data/data/repository/ProfileRepositoryImpl.kt new file mode 100644 index 00000000..6be4eea5 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/data/repository/ProfileRepositoryImpl.kt @@ -0,0 +1,252 @@ +package com.niyaj.data.data.repository + +import android.util.Patterns +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.result.ValidationResult +import com.niyaj.common.utils.Constants.RESTAURANT_ID +import com.niyaj.data.mapper.toEntity +import com.niyaj.data.repository.ProfileRepository +import com.niyaj.data.repository.validation.ProfileValidationRepository +import com.niyaj.data.utils.ProfileTestTags +import com.niyaj.database.dao.ProfileDao +import com.niyaj.database.model.ProfileEntity +import com.niyaj.database.model.asExternalModel +import com.niyaj.model.Profile +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.withContext + +class ProfileRepositoryImpl( + private val profileDao: ProfileDao, + @Dispatcher(PoposDispatchers.IO) + private val ioDispatcher: CoroutineDispatcher, +) : ProfileRepository, ProfileValidationRepository { + + override fun getProfileInfo(): Flow { + return profileDao.getProfileInfo().mapLatest { + it?.asExternalModel() + } + } + + override suspend fun insertOrUpdateProfile(profile: Profile): Resource { + return try { + withContext(ioDispatcher) { + val result = profileDao.insertOrUpdateProfile(profile.toEntity()) + + Resource.Success(result > 0) + } + } catch (e: Exception) { + Resource.Error(e.message) + } + } + + override suspend fun updateRestaurantLogo(imageName: String): Resource { + return withContext(ioDispatcher) { + try { + val profile = profileDao.getProfileById() + + val newProfile = ProfileEntity( + restaurantId = RESTAURANT_ID, + name = profile?.name ?: "", + email = profile?.email ?: "", + primaryPhone = profile?.primaryPhone ?: "", + secondaryPhone = profile?.secondaryPhone ?: "", + tagline = profile?.tagline ?: "", + description = profile?.description ?: "", + address = profile?.address ?: "", + logo = imageName, + printLogo = profile?.printLogo ?: "", + paymentQrCode = profile?.paymentQrCode ?: "", + createdAt = profile?.createdAt ?: System.currentTimeMillis().toString(), + updatedAt = System.currentTimeMillis().toString(), + ) + + val result = profileDao.insertOrUpdateProfile(newProfile) + + Resource.Success(result > 0) + } catch (e: Exception) { + Resource.Error(e.message) + } + } + } + + override suspend fun updatePrintLogo(imageName: String): Resource { + return withContext(ioDispatcher) { + try { + val profile = profileDao.getProfileById() + + val newProfile = ProfileEntity( + restaurantId = RESTAURANT_ID, + name = profile?.name ?: "", + email = profile?.email ?: "", + primaryPhone = profile?.primaryPhone ?: "", + secondaryPhone = profile?.secondaryPhone ?: "", + tagline = profile?.tagline ?: "", + description = profile?.description ?: "", + address = profile?.address ?: "", + logo = profile?.logo ?: "", + printLogo = imageName, + paymentQrCode = profile?.paymentQrCode ?: "", + createdAt = profile?.createdAt ?: System.currentTimeMillis().toString(), + updatedAt = System.currentTimeMillis().toString(), + ) + + val result = profileDao.insertOrUpdateProfile(newProfile) + + Resource.Success(result > 0) + } catch (e: Exception) { + Resource.Error(e.message) + } + } + } + + override fun validateName(name: String): ValidationResult { + if (name.isEmpty()) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.NAME_EMPTY_ERROR + ) + } + + if (name.length < 5) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.NAME_LENGTH_ERROR + ) + } + + if (name.any { it.isDigit() }) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.NAME_DIGITS_ERROR + ) + } + + return ValidationResult(true) + } + + override fun validateEmail(email: String): ValidationResult { + if (email.isEmpty()) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.EMAIL_EMPTY_ERROR + ) + } + + if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.EMAIL_NOT_VALID, + ) + } + + return ValidationResult(true) + } + + override fun validatePrimaryPhone(phone: String): ValidationResult { + + if (phone.isEmpty()) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.P_PHONE_EMPTY_ERROR + ) + } + + if (phone.any { it.isLetter() }) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.P_PHONE_CHAR_ERROR + ) + } + + if (phone.length != 10) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.P_PHONE_LENGTH_ERROR + ) + } + + return ValidationResult(true) + } + + override fun validateSecondaryPhone(phone: String): ValidationResult { + if (phone.isNotEmpty()) { + if (phone.any { it.isLetter() }) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.S_PHONE_CHAR_ERROR + ) + } + + if (phone.length != 10) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.S_PHONE_LENGTH_ERROR + ) + } + } + + return ValidationResult(true) + } + + override fun validateTagline(tagline: String): ValidationResult { + if (tagline.isEmpty()) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.TAG_EMPTY_ERROR + ) + } + + if (tagline.length > 40) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.TAG_LENGTH_ERROR + ) + } + + return ValidationResult(true) + } + + override fun validateDescription(description: String): ValidationResult { + if (description.isEmpty()) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.DESC_EMPTY_ERROR + ) + } + + if (description.length > 120) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.DESC_LENGTH_ERROR + ) + } + + return ValidationResult(true) + } + + override fun validateAddress(address: String): ValidationResult { + if (address.isEmpty()) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.ADDRESS_EMPTY_ERROR + ) + } + + return ValidationResult(true) + } + + override fun validateLogo(logo: String): ValidationResult { + if (logo.isEmpty()) { + return ValidationResult( + successful = false, + errorMessage = ProfileTestTags.LOGO_EMPTY_ERROR + ) + } + + return ValidationResult(true) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/di/AbsentModule.kt b/core/data/src/main/java/com/niyaj/data/di/AbsentModule.kt similarity index 51% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/di/AbsentModule.kt rename to core/data/src/main/java/com/niyaj/data/di/AbsentModule.kt index 06a8cdfe..651445d6 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/di/AbsentModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/AbsentModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.employee_absent.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.employee_absent.data.dao.AbsentDao -import com.niyaj.poposroom.features.employee_absent.data.repository.AbsentRepositoryImpl -import com.niyaj.poposroom.features.employee_absent.domain.repository.AbsentRepository -import com.niyaj.poposroom.features.employee_absent.domain.repository.AbsentValidationRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.AbsentRepositoryImpl +import com.niyaj.data.repository.AbsentRepository +import com.niyaj.data.repository.validation.AbsentValidationRepository +import com.niyaj.database.dao.AbsentDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object AbsentModule { - @Provides - fun provideAbsentDao(database: PoposDatabase) : AbsentDao { - return database.absentDao() - } - @Provides fun provideAbsentValidationRepository( absentDao: AbsentDao, diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/di/AddOnItemModule.kt b/core/data/src/main/java/com/niyaj/data/di/AddOnItemModule.kt similarity index 52% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/di/AddOnItemModule.kt rename to core/data/src/main/java/com/niyaj/data/di/AddOnItemModule.kt index 5e5a4cdb..7a47753c 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/di/AddOnItemModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/AddOnItemModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.addon_item.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.addon_item.data.dao.AddOnItemDao -import com.niyaj.poposroom.features.addon_item.data.repository.AddOnItemRepositoryImpl -import com.niyaj.poposroom.features.addon_item.domain.repository.AddOnItemRepository -import com.niyaj.poposroom.features.addon_item.domain.repository.AddOnItemValidationRepository -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.AddOnItemRepositoryImpl +import com.niyaj.data.repository.AddOnItemRepository +import com.niyaj.data.repository.validation.AddOnItemValidationRepository +import com.niyaj.database.dao.AddOnItemDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object AddOnItemModule { - @Provides - fun provideAddOnItemDao(database: PoposDatabase) : AddOnItemDao { - return database.addOnItemDao() - } - @Provides fun provideAddOnItemValidationRepository( addOnItemDao: AddOnItemDao, diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/di/AddressModule.kt b/core/data/src/main/java/com/niyaj/data/di/AddressModule.kt similarity index 52% rename from app/src/main/java/com/niyaj/poposroom/features/address/di/AddressModule.kt rename to core/data/src/main/java/com/niyaj/data/di/AddressModule.kt index c4f530af..eae17698 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/di/AddressModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/AddressModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.address.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.address.data.dao.AddressDao -import com.niyaj.poposroom.features.address.data.repository.AddressRepositoryImpl -import com.niyaj.poposroom.features.address.domain.repository.AddressRepository -import com.niyaj.poposroom.features.address.domain.repository.AddressValidationRepository -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.AddressRepositoryImpl +import com.niyaj.data.repository.AddressRepository +import com.niyaj.data.repository.validation.AddressValidationRepository +import com.niyaj.database.dao.AddressDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object AddressModule { - @Provides - fun provideAddressDao(database: PoposDatabase) : AddressDao { - return database.addressDao() - } - @Provides fun provideAddressValidationRepository( addressDao: AddressDao, @@ -37,5 +31,4 @@ object AddressModule { ): AddressRepository { return AddressRepositoryImpl(addressDao, ioDispatcher) } - } \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/di/CartModule.kt b/core/data/src/main/java/com/niyaj/data/di/CartModule.kt new file mode 100644 index 00000000..7b479da3 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/di/CartModule.kt @@ -0,0 +1,29 @@ +package com.niyaj.data.di + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.CartRepositoryImpl +import com.niyaj.data.repository.CartRepository +import com.niyaj.database.dao.CartDao +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.SelectedDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher + +@Module +@InstallIn(SingletonComponent::class) +object CartModule { + + @Provides + fun provideCartRepository( + cartDao: CartDao, + cartOrderDao: CartOrderDao, + selectedDao: SelectedDao, + @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, + ): CartRepository { + return CartRepositoryImpl(cartDao, cartOrderDao,selectedDao, ioDispatcher) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/di/CartOrderModule.kt b/core/data/src/main/java/com/niyaj/data/di/CartOrderModule.kt similarity index 54% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/di/CartOrderModule.kt rename to core/data/src/main/java/com/niyaj/data/di/CartOrderModule.kt index 123bd418..4acf688d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/di/CartOrderModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/CartOrderModule.kt @@ -1,15 +1,14 @@ -package com.niyaj.poposroom.features.cart_order.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.address.data.dao.AddressDao -import com.niyaj.poposroom.features.cart_order.data.dao.CartOrderDao -import com.niyaj.poposroom.features.cart_order.data.repository.CartOrderRepositoryImpl -import com.niyaj.poposroom.features.cart_order.domain.repository.CartOrderRepository -import com.niyaj.poposroom.features.cart_order.domain.repository.CartOrderValidationRepository -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.customer.data.dao.CustomerDao -import com.niyaj.poposroom.features.selected.data.dao.SelectedDao +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.CartOrderRepositoryImpl +import com.niyaj.data.repository.CartOrderRepository +import com.niyaj.data.repository.validation.CartOrderValidationRepository +import com.niyaj.database.dao.AddressDao +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.CustomerDao +import com.niyaj.database.dao.SelectedDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -20,11 +19,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object CartOrderModule { - @Provides - fun provideCartOrderDao(database: PoposDatabase) : CartOrderDao { - return database.cartOrderDao() - } - @Provides fun provideCartOrderRepository( cartOrderDao: CartOrderDao, diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/di/CategoryModule.kt b/core/data/src/main/java/com/niyaj/data/di/CategoryModule.kt similarity index 52% rename from app/src/main/java/com/niyaj/poposroom/features/category/di/CategoryModule.kt rename to core/data/src/main/java/com/niyaj/data/di/CategoryModule.kt index 369193a6..39e4a36e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/di/CategoryModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/CategoryModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.category.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.category.data.dao.CategoryDao -import com.niyaj.poposroom.features.category.data.repository.CategoryRepositoryImpl -import com.niyaj.poposroom.features.category.domain.repository.CategoryRepository -import com.niyaj.poposroom.features.category.domain.repository.CategoryValidationRepository -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.CategoryRepositoryImpl +import com.niyaj.data.repository.CategoryRepository +import com.niyaj.data.repository.validation.CategoryValidationRepository +import com.niyaj.database.dao.CategoryDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object CategoryModule { - @Provides - fun provideCategoryDao(database: PoposDatabase) : CategoryDao { - return database.categoryDao() - } - @Provides fun provideCategoryValidationRepository( categoryDao: CategoryDao, diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/di/ChargesModule.kt b/core/data/src/main/java/com/niyaj/data/di/ChargesModule.kt similarity index 57% rename from app/src/main/java/com/niyaj/poposroom/features/charges/di/ChargesModule.kt rename to core/data/src/main/java/com/niyaj/data/di/ChargesModule.kt index a8ae4490..3b9e8373 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/di/ChargesModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/ChargesModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.charges.di +package com.niyaj.data.di +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.ChargesRepositoryImpl +import com.niyaj.data.repository.ChargesRepository +import com.niyaj.data.repository.validation.ChargesValidationRepository import com.niyaj.poposroom.features.charges.data.dao.ChargesDao -import com.niyaj.poposroom.features.charges.data.repository.ChargesRepositoryImpl -import com.niyaj.poposroom.features.charges.domain.repository.ChargesRepository -import com.niyaj.poposroom.features.charges.domain.repository.ChargesValidationRepository -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object ChargesModule { - @Provides - fun provideChargesDao(database: PoposDatabase) : ChargesDao { - return database.chargesDao() - } - @Provides fun provideChargesValidationRepository( chargesDao: ChargesDao, diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/di/CustomerModule.kt b/core/data/src/main/java/com/niyaj/data/di/CustomerModule.kt similarity index 52% rename from app/src/main/java/com/niyaj/poposroom/features/customer/di/CustomerModule.kt rename to core/data/src/main/java/com/niyaj/data/di/CustomerModule.kt index e75f05d7..de951abd 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/di/CustomerModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/CustomerModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.customer.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.customer.data.dao.CustomerDao -import com.niyaj.poposroom.features.customer.data.repository.CustomerRepositoryImpl -import com.niyaj.poposroom.features.customer.domain.repository.CustomerRepository -import com.niyaj.poposroom.features.customer.domain.repository.CustomerValidationRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.CustomerRepositoryImpl +import com.niyaj.data.repository.CustomerRepository +import com.niyaj.data.repository.validation.CustomerValidationRepository +import com.niyaj.database.dao.CustomerDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object CustomerModule { - @Provides - fun provideCustomerDao(database: PoposDatabase) : CustomerDao { - return database.customerDao() - } - @Provides fun provideCustomerValidationRepository( customerDao: CustomerDao, diff --git a/core/data/src/main/java/com/niyaj/data/di/DataModule.kt b/core/data/src/main/java/com/niyaj/data/di/DataModule.kt new file mode 100644 index 00000000..23e4aeeb --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/di/DataModule.kt @@ -0,0 +1,18 @@ +package com.niyaj.data.di + +import com.niyaj.data.utils.ConnectivityManagerNetworkMonitor +import com.niyaj.data.utils.NetworkMonitor +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +interface DataModule { + + @Binds + fun bindsNetworkMonitor( + networkMonitor: ConnectivityManagerNetworkMonitor, + ): NetworkMonitor +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/di/EmployeeModule.kt b/core/data/src/main/java/com/niyaj/data/di/EmployeeModule.kt similarity index 52% rename from app/src/main/java/com/niyaj/poposroom/features/employee/di/EmployeeModule.kt rename to core/data/src/main/java/com/niyaj/data/di/EmployeeModule.kt index 8b8f3c69..690fec19 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/di/EmployeeModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/EmployeeModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.employee.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.employee.data.dao.EmployeeDao -import com.niyaj.poposroom.features.employee.data.repository.EmployeeRepositoryImpl -import com.niyaj.poposroom.features.employee.domain.repository.EmployeeRepository -import com.niyaj.poposroom.features.employee.domain.repository.EmployeeValidationRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.EmployeeRepositoryImpl +import com.niyaj.data.repository.EmployeeRepository +import com.niyaj.data.repository.EmployeeValidationRepository +import com.niyaj.database.dao.EmployeeDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object EmployeeModule { - @Provides - fun provideEmployeeDao(database: PoposDatabase) : EmployeeDao { - return database.employeeDao() - } - @Provides fun provideEmployeeValidationRepository( employeeDao: EmployeeDao, diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/di/ExpenseModule.kt b/core/data/src/main/java/com/niyaj/data/di/ExpenseModule.kt similarity index 52% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/di/ExpenseModule.kt rename to core/data/src/main/java/com/niyaj/data/di/ExpenseModule.kt index 22004ac2..bf899282 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/di/ExpenseModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/ExpenseModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.expenses.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.expenses.data.dao.ExpenseDao -import com.niyaj.poposroom.features.expenses.data.repository.ExpenseRepositoryImpl -import com.niyaj.poposroom.features.expenses.domain.repository.ExpenseRepository -import com.niyaj.poposroom.features.expenses.domain.repository.ExpenseValidationRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.ExpenseRepositoryImpl +import com.niyaj.data.repository.ExpenseRepository +import com.niyaj.data.repository.ExpenseValidationRepository +import com.niyaj.database.dao.ExpenseDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object ExpenseModule { - @Provides - fun provideExpenseDao(database: PoposDatabase) : ExpenseDao { - return database.expenseDao() - } - @Provides fun provideExpenseValidationRepository( expenseDao: ExpenseDao, diff --git a/core/data/src/main/java/com/niyaj/data/di/MainFeedModule.kt b/core/data/src/main/java/com/niyaj/data/di/MainFeedModule.kt new file mode 100644 index 00000000..b36f7d8f --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/di/MainFeedModule.kt @@ -0,0 +1,26 @@ +package com.niyaj.data.di + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.MainFeedRepositoryImpl +import com.niyaj.data.repository.MainFeedRepository +import com.niyaj.database.dao.MainFeedDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher + +@Module +@InstallIn(SingletonComponent::class) +object MainFeedModule { + + @Provides + fun provideMainFeedRepository( + mainFeedDao: MainFeedDao, + @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, + ): MainFeedRepository { + return MainFeedRepositoryImpl(mainFeedDao, ioDispatcher) + } + +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/di/OrderModule.kt b/core/data/src/main/java/com/niyaj/data/di/OrderModule.kt new file mode 100644 index 00000000..d330b858 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/di/OrderModule.kt @@ -0,0 +1,30 @@ +package com.niyaj.data.di + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.OrderRepositoryImpl +import com.niyaj.data.repository.OrderRepository +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.OrderDao +import com.niyaj.database.dao.SelectedDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher + +@Module +@InstallIn(SingletonComponent::class) +object OrderModule { + + @Provides + fun provideOrderRepository( + orderDao: OrderDao, + cartOrderDao: CartOrderDao, + selectedDao: SelectedDao, + @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, + ): OrderRepository { + return OrderRepositoryImpl(orderDao, cartOrderDao, selectedDao, ioDispatcher) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/di/PaymentModule.kt b/core/data/src/main/java/com/niyaj/data/di/PaymentModule.kt similarity index 56% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/di/PaymentModule.kt rename to core/data/src/main/java/com/niyaj/data/di/PaymentModule.kt index c2d5127a..f6d8259a 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/di/PaymentModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/PaymentModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.employee_payment.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.PaymentRepositoryImpl +import com.niyaj.data.repository.PaymentRepository +import com.niyaj.data.repository.validation.PaymentValidationRepository import com.niyaj.poposroom.features.employee_payment.data.dao.PaymentDao -import com.niyaj.poposroom.features.employee_payment.data.repository.PaymentRepositoryImpl -import com.niyaj.poposroom.features.employee_payment.domain.repository.PaymentRepository -import com.niyaj.poposroom.features.employee_payment.domain.repository.PaymentValidationRepository import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object PaymentModule { - @Provides - fun providePaymentDao(database: PoposDatabase) : PaymentDao { - return database.paymentDao() - } - @Provides fun providePaymentValidationRepository( paymentDao: PaymentDao, diff --git a/core/data/src/main/java/com/niyaj/data/di/PrintModule.kt b/core/data/src/main/java/com/niyaj/data/di/PrintModule.kt new file mode 100644 index 00000000..77d4ba8b --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/di/PrintModule.kt @@ -0,0 +1,27 @@ +package com.niyaj.data.di + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.PrintRepositoryImpl +import com.niyaj.data.repository.PrintRepository +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.PrintDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher + +@Module +@InstallIn(SingletonComponent::class) +object PrintModule { + + @Provides + fun providePrintRepository( + orderDao: PrintDao, + cartOrderDao: CartOrderDao, + @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, + ): PrintRepository { + return PrintRepositoryImpl(orderDao, cartOrderDao, ioDispatcher) + } +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/di/PrinterModule.kt b/core/data/src/main/java/com/niyaj/data/di/PrinterModule.kt new file mode 100644 index 00000000..8e980540 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/di/PrinterModule.kt @@ -0,0 +1,34 @@ +package com.niyaj.data.di + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.PrinterRepositoryImpl +import com.niyaj.data.data.repository.PrinterValidationRepositoryImpl +import com.niyaj.data.repository.PrinterRepository +import com.niyaj.data.repository.validation.PrinterValidationRepository +import com.niyaj.database.dao.PrinterDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object PrinterModule { + + @Provides + fun providePrinterValidationRepository(): PrinterValidationRepository { + return PrinterValidationRepositoryImpl() + } + + @Provides + @Singleton + fun providePrinterRepository( + printerDao: PrinterDao, + @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, + ): PrinterRepository { + return PrinterRepositoryImpl(printerDao, ioDispatcher) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/di/ProductModule.kt b/core/data/src/main/java/com/niyaj/data/di/ProductModule.kt similarity index 52% rename from app/src/main/java/com/niyaj/poposroom/features/product/di/ProductModule.kt rename to core/data/src/main/java/com/niyaj/data/di/ProductModule.kt index 1adfb263..187961e3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/di/ProductModule.kt +++ b/core/data/src/main/java/com/niyaj/data/di/ProductModule.kt @@ -1,12 +1,11 @@ -package com.niyaj.poposroom.features.product.di +package com.niyaj.data.di -import com.niyaj.poposroom.features.common.database.PoposDatabase -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.product.data.dao.ProductDao -import com.niyaj.poposroom.features.product.data.repository.ProductRepositoryImpl -import com.niyaj.poposroom.features.product.domain.repository.ProductRepository -import com.niyaj.poposroom.features.product.domain.repository.ProductValidationRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.ProductRepositoryImpl +import com.niyaj.data.repository.ProductRepository +import com.niyaj.data.repository.validation.ProductValidationRepository +import com.niyaj.database.dao.ProductDao import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -17,11 +16,6 @@ import kotlinx.coroutines.CoroutineDispatcher @InstallIn(SingletonComponent::class) object ProductModule { - @Provides - fun provideProductDao(database: PoposDatabase) : ProductDao { - return database.productDao() - } - @Provides fun provideProductValidationRepository( productDao: ProductDao, diff --git a/core/data/src/main/java/com/niyaj/data/di/ProfileModule.kt b/core/data/src/main/java/com/niyaj/data/di/ProfileModule.kt new file mode 100644 index 00000000..d3c08b97 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/di/ProfileModule.kt @@ -0,0 +1,35 @@ +package com.niyaj.data.di + +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.data.repository.ProfileRepositoryImpl +import com.niyaj.data.repository.ProfileRepository +import com.niyaj.data.repository.validation.ProfileValidationRepository +import com.niyaj.database.dao.ProfileDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.CoroutineDispatcher + +@Module +@InstallIn(SingletonComponent::class) +object ProfileModule { + + @Provides + fun profileValidationRepository( + profileDao: ProfileDao, + @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, + ): ProfileValidationRepository { + return ProfileRepositoryImpl(profileDao, ioDispatcher) + } + + @Provides + fun provideProfileRepository( + profileDao: ProfileDao, + @Dispatcher(PoposDispatchers.IO) ioDispatcher: CoroutineDispatcher, + ): ProfileRepository { + return ProfileRepositoryImpl(profileDao, ioDispatcher) + } + +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/AbsentMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/AbsentMapper.kt new file mode 100644 index 00000000..a0974f58 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/AbsentMapper.kt @@ -0,0 +1,15 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.AbsentEntity +import com.niyaj.model.Absent + +fun Absent.toEntity(): AbsentEntity { + return AbsentEntity( + absentId = this.absentId, + employeeId = this.employeeId, + absentReason = this.absentReason, + absentDate = this.absentDate, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/AddOnItemMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/AddOnItemMapper.kt new file mode 100644 index 00000000..c8a501a5 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/AddOnItemMapper.kt @@ -0,0 +1,16 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.AddOnItemEntity +import com.niyaj.model.AddOnItem + +fun AddOnItem.toEntity(): AddOnItemEntity { + return AddOnItemEntity( + itemId = this.itemId, + itemName = this.itemName, + itemPrice = this.itemPrice, + isApplicable = this.isApplicable, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} + diff --git a/core/data/src/main/java/com/niyaj/data/mapper/AddressMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/AddressMapper.kt new file mode 100644 index 00000000..1ddace4b --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/AddressMapper.kt @@ -0,0 +1,14 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.AddressEntity +import com.niyaj.model.Address + +fun Address.toEntity(): AddressEntity { + return AddressEntity( + addressId = this.addressId, + addressName = this.addressName, + shortName = this.shortName, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/CategoryMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/CategoryMapper.kt new file mode 100644 index 00000000..c722aa5a --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/CategoryMapper.kt @@ -0,0 +1,14 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.CategoryEntity +import com.niyaj.model.Category + +fun Category.toEntity(): CategoryEntity { + return CategoryEntity( + categoryId = this.categoryId, + categoryName = this.categoryName, + isAvailable = this.isAvailable, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/ChargesMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/ChargesMapper.kt new file mode 100644 index 00000000..90b64131 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/ChargesMapper.kt @@ -0,0 +1,15 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.ChargesEntity +import com.niyaj.model.Charges + +fun Charges.toEntity(): ChargesEntity { + return ChargesEntity( + chargesId = this.chargesId, + chargesName = this.chargesName, + chargesPrice = this.chargesPrice, + isApplicable = this.isApplicable, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/CustomerMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/CustomerMapper.kt new file mode 100644 index 00000000..f4921c65 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/CustomerMapper.kt @@ -0,0 +1,15 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.CustomerEntity +import com.niyaj.model.Customer + +fun Customer.toEntity(): CustomerEntity { + return CustomerEntity( + customerId = this.customerId, + customerName = this.customerName, + customerPhone = this.customerPhone, + customerEmail = this.customerEmail, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/EmployeeMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/EmployeeMapper.kt new file mode 100644 index 00000000..32c4410c --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/EmployeeMapper.kt @@ -0,0 +1,20 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.EmployeeEntity +import com.niyaj.model.Employee + +fun Employee.toEntity(): EmployeeEntity { + return EmployeeEntity( + employeeId = this.employeeId, + employeeName = this.employeeName, + employeePhone = this.employeePhone, + employeeSalary = this.employeeSalary, + employeePosition = this.employeePosition, + employeeJoinedDate = this.employeeJoinedDate, + employeeEmail = this.employeeEmail, + employeeSalaryType = this.employeeSalaryType, + employeeType = this.employeeType, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/ExpenseMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/ExpenseMapper.kt new file mode 100644 index 00000000..2e191ff0 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/ExpenseMapper.kt @@ -0,0 +1,16 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.ExpenseEntity +import com.niyaj.model.Expense + +fun Expense.toEntity(): ExpenseEntity { + return ExpenseEntity( + expenseId = this.expenseId, + expenseName = this.expenseName, + expenseAmount = this.expenseAmount, + expenseDate = this.expenseDate, + expenseNote = this.expenseNote, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/PayementMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/PayementMapper.kt new file mode 100644 index 00000000..7cb8be98 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/PayementMapper.kt @@ -0,0 +1,18 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.PaymentEntity +import com.niyaj.model.Payment + +fun Payment.toEntity(): PaymentEntity { + return PaymentEntity( + paymentId = this.paymentId, + employeeId = this.employeeId, + paymentAmount = this.paymentAmount, + paymentDate = this.paymentDate, + paymentType = this.paymentType, + paymentMode = this.paymentMode, + paymentNote = this.paymentNote, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/PrinterMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/PrinterMapper.kt new file mode 100644 index 00000000..1e3ef292 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/PrinterMapper.kt @@ -0,0 +1,22 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.PrinterEntity +import com.niyaj.model.Printer + +fun Printer.toEntity(): PrinterEntity { + return PrinterEntity( + printerId = this.printerId, + printerDpi = this.printerDpi, + printerWidth = this.printerWidth, + printerNbrLines = this.printerNbrLines, + productNameLength = this.productNameLength, + productWiseReportLimit = this.productWiseReportLimit, + addressWiseReportLimit = this.addressWiseReportLimit, + customerWiseReportLimit = this.customerWiseReportLimit, + printQRCode = this.printQRCode, + printResLogo = this.printResLogo, + printWelcomeText = this.printWelcomeText, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/ProductMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/ProductMapper.kt new file mode 100644 index 00000000..a4d22ca0 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/ProductMapper.kt @@ -0,0 +1,17 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.ProductEntity +import com.niyaj.model.Product + +fun Product.toEntity(): ProductEntity { + return ProductEntity( + productId = this.productId, + categoryId = this.categoryId, + productName = this.productName, + productPrice = this.productPrice, + productDescription = this.productDescription, + productAvailability = this.productAvailability, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/ProfileMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/ProfileMapper.kt new file mode 100644 index 00000000..3d985986 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/ProfileMapper.kt @@ -0,0 +1,22 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.ProfileEntity +import com.niyaj.model.Profile + +fun Profile.toEntity(): ProfileEntity { + return ProfileEntity( + restaurantId = this.restaurantId, + name = this.name, + email = this.email, + primaryPhone = this.primaryPhone, + secondaryPhone = this.secondaryPhone, + tagline = this.tagline, + description = this.description, + address = this.address, + logo = this.logo, + printLogo = this.printLogo, + paymentQrCode = this.paymentQrCode, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/mapper/SelectedMapper.kt b/core/data/src/main/java/com/niyaj/data/mapper/SelectedMapper.kt new file mode 100644 index 00000000..994a76f4 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/mapper/SelectedMapper.kt @@ -0,0 +1,11 @@ +package com.niyaj.data.mapper + +import com.niyaj.database.model.SelectedEntity +import com.niyaj.model.Selected + +fun Selected.toEntity(): SelectedEntity { + return SelectedEntity( + selectedId = this.selectedId, + orderId = this.orderId + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/repository/AbsentRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/AbsentRepository.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/repository/AbsentRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/AbsentRepository.kt index 810cece0..004fae36 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/repository/AbsentRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/AbsentRepository.kt @@ -1,9 +1,9 @@ -package com.niyaj.poposroom.features.employee_absent.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee_absent.domain.model.Absent -import com.niyaj.poposroom.features.employee_absent.domain.model.EmployeeWithAbsent +import com.niyaj.common.result.Resource +import com.niyaj.model.Absent +import com.niyaj.model.Employee +import com.niyaj.model.EmployeeWithAbsents import kotlinx.coroutines.flow.Flow interface AbsentRepository { @@ -12,7 +12,7 @@ interface AbsentRepository { suspend fun getEmployeeById(employeeId: Int) : Employee? - suspend fun getAllEmployeeAbsents(searchText: String): Flow> + suspend fun getAllEmployeeAbsents(searchText: String): Flow> suspend fun getAllAbsent(searchText: String): Flow> diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/repository/AddOnItemRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/AddOnItemRepository.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/repository/AddOnItemRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/AddOnItemRepository.kt index 21eeeb65..f8d80ed3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/repository/AddOnItemRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/AddOnItemRepository.kt @@ -1,7 +1,7 @@ -package com.niyaj.poposroom.features.addon_item.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.common.utils.Resource +import com.niyaj.common.result.Resource +import com.niyaj.model.AddOnItem import kotlinx.coroutines.flow.Flow diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/domain/repository/AddressRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/AddressRepository.kt similarity index 74% rename from app/src/main/java/com/niyaj/poposroom/features/address/domain/repository/AddressRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/AddressRepository.kt index ce4ca6ba..d525c438 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/domain/repository/AddressRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/AddressRepository.kt @@ -1,7 +1,7 @@ -package com.niyaj.poposroom.features.address.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.common.utils.Resource +import com.niyaj.common.result.Resource +import com.niyaj.model.Address import kotlinx.coroutines.flow.Flow interface AddressRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/repository/CartOrderRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/CartOrderRepository.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/repository/CartOrderRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/CartOrderRepository.kt index bdf9279b..8aa2e740 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/repository/CartOrderRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/CartOrderRepository.kt @@ -1,10 +1,10 @@ -package com.niyaj.poposroom.features.cart_order.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrder -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.customer.domain.model.Customer -import com.niyaj.poposroom.features.selected.domain.model.Selected +import com.niyaj.common.result.Resource +import com.niyaj.model.Address +import com.niyaj.model.CartOrder +import com.niyaj.model.Customer +import com.niyaj.model.Selected import kotlinx.coroutines.flow.Flow interface CartOrderRepository { @@ -19,7 +19,7 @@ interface CartOrderRepository { suspend fun getAllCustomer(searchText: String): Flow> - suspend fun getAllCartOrders(searchText: String): Flow> + suspend fun getAllCartOrders(searchText: String, viewAll: Boolean): Flow> suspend fun getCartOrderById(orderId: Int): Resource diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/repository/CartRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/CartRepository.kt similarity index 69% rename from app/src/main/java/com/niyaj/poposroom/features/cart/domain/repository/CartRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/CartRepository.kt index 7c294e83..92fbee5d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/repository/CartRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/CartRepository.kt @@ -1,16 +1,16 @@ -package com.niyaj.poposroom.features.cart.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.cart.domain.model.CartItem -import com.niyaj.poposroom.features.cart.domain.model.OrderWithCart -import com.niyaj.poposroom.features.common.utils.Resource +import com.niyaj.common.result.Resource +import com.niyaj.model.AddOnItem +import com.niyaj.model.CartItem +import com.niyaj.model.OrderWithCartItems import kotlinx.coroutines.flow.Flow interface CartRepository { fun getAllAddOnItems(): Flow> - suspend fun getAllCartOrders(): Flow> + suspend fun getAllCartOrders(): Flow> suspend fun getAllDineInCart(): Flow> diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/domain/repository/CategoryRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/CategoryRepository.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/category/domain/repository/CategoryRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/CategoryRepository.kt index b61d4b37..7813a58e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/domain/repository/CategoryRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/CategoryRepository.kt @@ -1,7 +1,7 @@ -package com.niyaj.poposroom.features.category.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.common.utils.Resource +import com.niyaj.common.result.Resource +import com.niyaj.model.Category import kotlinx.coroutines.flow.Flow interface CategoryRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/domain/repository/ChargesRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/ChargesRepository.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/charges/domain/repository/ChargesRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/ChargesRepository.kt index a1a31abb..2dac6d72 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/domain/repository/ChargesRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/ChargesRepository.kt @@ -1,7 +1,7 @@ -package com.niyaj.poposroom.features.charges.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.common.utils.Resource +import com.niyaj.common.result.Resource +import com.niyaj.model.Charges import kotlinx.coroutines.flow.Flow interface ChargesRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/domain/repository/CustomerRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/CustomerRepository.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/customer/domain/repository/CustomerRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/CustomerRepository.kt index c9ff8395..6798dfaa 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/domain/repository/CustomerRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/CustomerRepository.kt @@ -1,7 +1,7 @@ -package com.niyaj.poposroom.features.customer.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.customer.domain.model.Customer +import com.niyaj.common.result.Resource +import com.niyaj.model.Customer import kotlinx.coroutines.flow.Flow interface CustomerRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/repository/EmployeeRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/EmployeeRepository.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/employee/domain/repository/EmployeeRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/EmployeeRepository.kt index 47db7266..ed9087e3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/repository/EmployeeRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/EmployeeRepository.kt @@ -1,7 +1,7 @@ -package com.niyaj.poposroom.features.employee.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.employee.domain.model.Employee +import com.niyaj.common.result.Resource +import com.niyaj.model.Employee import kotlinx.coroutines.flow.Flow interface EmployeeRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/repository/EmployeeValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/EmployeeValidationRepository.kt similarity index 74% rename from app/src/main/java/com/niyaj/poposroom/features/employee/domain/repository/EmployeeValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/EmployeeValidationRepository.kt index 97671bf8..b1bf3c8e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/repository/EmployeeValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/EmployeeValidationRepository.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.employee.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult interface EmployeeValidationRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/domain/repository/ExpenseRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/ExpenseRepository.kt similarity index 82% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/domain/repository/ExpenseRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/ExpenseRepository.kt index 8609c6a8..c89ae523 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/domain/repository/ExpenseRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/ExpenseRepository.kt @@ -1,7 +1,7 @@ -package com.niyaj.poposroom.features.expenses.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.expenses.domain.model.Expense +import com.niyaj.common.result.Resource +import com.niyaj.model.Expense import kotlinx.coroutines.flow.Flow interface ExpenseRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/domain/repository/ExpenseValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/ExpenseValidationRepository.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/domain/repository/ExpenseValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/ExpenseValidationRepository.kt index 334e2a74..5fcc934e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/domain/repository/ExpenseValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/ExpenseValidationRepository.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.expenses.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult interface ExpenseValidationRepository { fun validateExpenseName(expenseName: String): ValidationResult diff --git a/core/data/src/main/java/com/niyaj/data/repository/MainFeedRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/MainFeedRepository.kt new file mode 100644 index 00000000..58816d63 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/repository/MainFeedRepository.kt @@ -0,0 +1,15 @@ +package com.niyaj.data.repository + +import com.niyaj.model.Category +import com.niyaj.model.ProductWithFlowQuantity +import com.niyaj.model.Selected +import kotlinx.coroutines.flow.Flow + +interface MainFeedRepository { + + fun getAllCategory(): Flow> + + suspend fun getAllProduct(searchText: String, selectedCategory: Int = 0): Flow> + + fun getSelectedOrder(): Flow +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/domain/repository/OrderRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/OrderRepository.kt similarity index 56% rename from app/src/main/java/com/niyaj/poposroom/features/order/domain/repository/OrderRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/OrderRepository.kt index e1553c83..10a52f36 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/domain/repository/OrderRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/OrderRepository.kt @@ -1,9 +1,9 @@ -package com.niyaj.poposroom.features.order.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.order.domain.model.Order -import com.niyaj.poposroom.features.order.domain.model.OrderDetails +import com.niyaj.common.result.Resource +import com.niyaj.model.Charges +import com.niyaj.model.Order +import com.niyaj.model.OrderDetails import kotlinx.coroutines.flow.Flow interface OrderRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/repository/PaymentRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/PaymentRepository.kt similarity index 61% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/repository/PaymentRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/PaymentRepository.kt index e54c8fde..62c71e0e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/repository/PaymentRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/PaymentRepository.kt @@ -1,12 +1,13 @@ -package com.niyaj.poposroom.features.employee_payment.domain.repository - -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee_payment.domain.model.CalculatedSalary -import com.niyaj.poposroom.features.employee_payment.domain.model.EmployeeWithPayment -import com.niyaj.poposroom.features.employee_payment.domain.model.Payment -import com.niyaj.poposroom.features.employee_payment.domain.model.SalaryCalculableDate -import com.niyaj.poposroom.features.employee_payment.domain.model.SalaryCalculation +package com.niyaj.data.repository + +import com.niyaj.common.result.Resource +import com.niyaj.model.CalculatedSalary +import com.niyaj.model.Employee +import com.niyaj.model.EmployeeWithPayment +import com.niyaj.model.EmployeeWithPayments +import com.niyaj.model.Payment +import com.niyaj.model.SalaryCalculableDate +import com.niyaj.model.SalaryCalculation import kotlinx.coroutines.flow.Flow interface PaymentRepository { @@ -15,7 +16,7 @@ interface PaymentRepository { suspend fun getEmployeeById(employeeId: Int) : Employee? - suspend fun getAllEmployeePayments(searchText: String): Flow> + suspend fun getAllEmployeePayments(searchText: String): Flow> suspend fun getAllPayment(searchText: String): Flow> diff --git a/core/data/src/main/java/com/niyaj/data/repository/PrintRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/PrintRepository.kt new file mode 100644 index 00000000..1561feff --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/repository/PrintRepository.kt @@ -0,0 +1,11 @@ +package com.niyaj.data.repository + +import com.niyaj.model.OrderDetails +import kotlinx.coroutines.flow.Flow + +interface PrintRepository { + + suspend fun getOrderDetail(orderId: Int): OrderDetails + + suspend fun getOrderDetails(orderIds: List): Flow> +} \ No newline at end of file diff --git a/core/data/src/main/java/com/niyaj/data/repository/PrinterRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/PrinterRepository.kt new file mode 100644 index 00000000..b02336f2 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/repository/PrinterRepository.kt @@ -0,0 +1,15 @@ +package com.niyaj.data.repository + +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.Constants.PRINTER_ID +import com.niyaj.model.Printer +import kotlinx.coroutines.flow.Flow + +interface PrinterRepository { + + suspend fun getPrinter(printerId : String = PRINTER_ID): Printer + + suspend fun getPrinterInfo(printerId: String = PRINTER_ID): Flow + + suspend fun addOrUpdatePrinterInfo(printer : Printer): Resource +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/domain/repository/ProductRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/ProductRepository.kt similarity index 68% rename from app/src/main/java/com/niyaj/poposroom/features/product/domain/repository/ProductRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/ProductRepository.kt index 43e4ceb2..dbe40f70 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/domain/repository/ProductRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/ProductRepository.kt @@ -1,9 +1,9 @@ -package com.niyaj.poposroom.features.product.domain.repository +package com.niyaj.data.repository -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.product.domain.model.CategoryWithProduct -import com.niyaj.poposroom.features.product.domain.model.Product +import com.niyaj.common.result.Resource +import com.niyaj.model.Category +import com.niyaj.model.CategoryWithProducts +import com.niyaj.model.Product import kotlinx.coroutines.flow.Flow interface ProductRepository { @@ -12,7 +12,7 @@ interface ProductRepository { suspend fun getCategoryById(categoryId: Int) : Category? - suspend fun getAllCategoryProducts(searchText: String): Flow> + suspend fun getAllCategoryProducts(searchText: String): Flow> suspend fun getAllProduct(searchText: String, selectedCategory: Int = 0): Flow> diff --git a/core/data/src/main/java/com/niyaj/data/repository/ProfileRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/ProfileRepository.kt new file mode 100644 index 00000000..a628466a --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/repository/ProfileRepository.kt @@ -0,0 +1,16 @@ +package com.niyaj.data.repository + +import com.niyaj.common.result.Resource +import com.niyaj.model.Profile +import kotlinx.coroutines.flow.Flow + +interface ProfileRepository { + + fun getProfileInfo(): Flow + + suspend fun updateRestaurantLogo(imageName: String): Resource + + suspend fun updatePrintLogo(imageName: String): Resource + + suspend fun insertOrUpdateProfile(profile: Profile): Resource +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/repository/AbsentValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/AbsentValidationRepository.kt similarity index 62% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/repository/AbsentValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/validation/AbsentValidationRepository.kt index a3af6d9b..2db32257 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/repository/AbsentValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/AbsentValidationRepository.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.employee_absent.domain.repository +package com.niyaj.data.repository.validation -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult interface AbsentValidationRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/repository/AddOnItemValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/AddOnItemValidationRepository.kt similarity index 59% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/repository/AddOnItemValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/validation/AddOnItemValidationRepository.kt index 2d891c35..0e28e0b5 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/repository/AddOnItemValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/AddOnItemValidationRepository.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.addon_item.domain.repository +package com.niyaj.data.repository.validation -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult interface AddOnItemValidationRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/domain/repository/AddressValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/AddressValidationRepository.kt similarity index 63% rename from app/src/main/java/com/niyaj/poposroom/features/address/domain/repository/AddressValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/validation/AddressValidationRepository.kt index c7b104b7..6d49e385 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/domain/repository/AddressValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/AddressValidationRepository.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.address.domain.repository +package com.niyaj.data.repository.validation -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult interface AddressValidationRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/repository/CartOrderValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/CartOrderValidationRepository.kt similarity index 67% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/repository/CartOrderValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/validation/CartOrderValidationRepository.kt index ce3c4edd..119d2e06 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/repository/CartOrderValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/CartOrderValidationRepository.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.cart_order.domain.repository +package com.niyaj.data.repository.validation -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult interface CartOrderValidationRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/domain/repository/CategoryValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/CategoryValidationRepository.kt similarity index 53% rename from app/src/main/java/com/niyaj/poposroom/features/category/domain/repository/CategoryValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/validation/CategoryValidationRepository.kt index 430b5c7d..650f2985 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/domain/repository/CategoryValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/CategoryValidationRepository.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.category.domain.repository +package com.niyaj.data.repository.validation -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult interface CategoryValidationRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/domain/repository/ChargesValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/ChargesValidationRepository.kt similarity index 62% rename from app/src/main/java/com/niyaj/poposroom/features/charges/domain/repository/ChargesValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/validation/ChargesValidationRepository.kt index 3ce03ecd..ab3b2cce 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/domain/repository/ChargesValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/ChargesValidationRepository.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.charges.domain.repository +package com.niyaj.data.repository.validation -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult interface ChargesValidationRepository { diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/domain/repository/CustomerValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/CustomerValidationRepository.kt similarity index 87% rename from app/src/main/java/com/niyaj/poposroom/features/customer/domain/repository/CustomerValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/validation/CustomerValidationRepository.kt index ddb4d692..7e53688c 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/domain/repository/CustomerValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/CustomerValidationRepository.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.customer.domain.repository +package com.niyaj.data.repository.validation -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult /** * Interface for validating customer data before saving to database. diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/repository/PaymentValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/PaymentValidationRepository.kt similarity index 63% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/repository/PaymentValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/validation/PaymentValidationRepository.kt index b086a0f4..041a5b8b 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/repository/PaymentValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/PaymentValidationRepository.kt @@ -1,8 +1,8 @@ -package com.niyaj.poposroom.features.employee_payment.domain.repository +package com.niyaj.data.repository.validation -import com.niyaj.poposroom.features.common.utils.ValidationResult -import com.niyaj.poposroom.features.employee.domain.utils.PaymentMode -import com.niyaj.poposroom.features.employee.domain.utils.PaymentType +import com.niyaj.common.result.ValidationResult +import com.niyaj.model.PaymentMode +import com.niyaj.model.PaymentType interface PaymentValidationRepository { diff --git a/core/data/src/main/java/com/niyaj/data/repository/validation/PrinterValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/PrinterValidationRepository.kt new file mode 100644 index 00000000..6817abf0 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/PrinterValidationRepository.kt @@ -0,0 +1,21 @@ +package com.niyaj.data.repository.validation + +import com.niyaj.common.result.ValidationResult + +interface PrinterValidationRepository { + + fun validatePrinterDpi(dpi: Int): ValidationResult + + fun validatePrinterWidth(width: Float): ValidationResult + + fun validatePrinterNbrLines(lines: Int): ValidationResult + + fun validateProductNameLength(length: Int): ValidationResult + + fun validateProductReportLimit(limit: Int): ValidationResult + + fun validateAddressReportLimit(limit: Int): ValidationResult + + fun validateCustomerReportLimit(limit: Int): ValidationResult + +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/domain/repository/ProductValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/ProductValidationRepository.kt similarity index 54% rename from app/src/main/java/com/niyaj/poposroom/features/product/domain/repository/ProductValidationRepository.kt rename to core/data/src/main/java/com/niyaj/data/repository/validation/ProductValidationRepository.kt index 64923337..bc09cf65 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/domain/repository/ProductValidationRepository.kt +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/ProductValidationRepository.kt @@ -1,10 +1,10 @@ -package com.niyaj.poposroom.features.product.domain.repository +package com.niyaj.data.repository.validation -import com.niyaj.poposroom.features.common.utils.ValidationResult +import com.niyaj.common.result.ValidationResult interface ProductValidationRepository { - fun validateCategoryName(categoryId: Int): ValidationResult + fun validateCategoryId(categoryId: Int): ValidationResult suspend fun validateProductName(productName: String, productId: Int? = null): ValidationResult diff --git a/core/data/src/main/java/com/niyaj/data/repository/validation/ProfileValidationRepository.kt b/core/data/src/main/java/com/niyaj/data/repository/validation/ProfileValidationRepository.kt new file mode 100644 index 00000000..1dc4403d --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/repository/validation/ProfileValidationRepository.kt @@ -0,0 +1,23 @@ +package com.niyaj.data.repository.validation + +import com.niyaj.common.result.ValidationResult + +interface ProfileValidationRepository { + + fun validateName(name: String): ValidationResult + + fun validateEmail(email: String): ValidationResult + + fun validatePrimaryPhone(phone: String): ValidationResult + + fun validateSecondaryPhone(phone: String): ValidationResult + + fun validateTagline(tagline: String): ValidationResult + + fun validateDescription(description: String): ValidationResult + + fun validateAddress(address: String): ValidationResult + + fun validateLogo(logo: String): ValidationResult + +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/utils/AbsentScreenTags.kt b/core/data/src/main/java/com/niyaj/data/utils/AbsentScreenTags.kt similarity index 94% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/utils/AbsentScreenTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/AbsentScreenTags.kt index 771658ba..b73d5db6 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/utils/AbsentScreenTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/AbsentScreenTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_absent.domain.utils +package com.niyaj.data.utils object AbsentScreenTags { diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/utils/AddOnConstants.kt b/core/data/src/main/java/com/niyaj/data/utils/AddOnTestTags.kt similarity index 87% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/utils/AddOnConstants.kt rename to core/data/src/main/java/com/niyaj/data/utils/AddOnTestTags.kt index 8d42047c..fd25202e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/utils/AddOnConstants.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/AddOnTestTags.kt @@ -1,16 +1,16 @@ -package com.niyaj.poposroom.features.addon_item.domain.utils +package com.niyaj.data.utils -object AddOnConstants { +object AddOnTestTags { const val ADDON_WHITELIST_ITEM = "Cold" const val ADDON_SCREEN_TITLE = "AddOn Items" const val ADDON_NOT_AVAIlABLE = "AddOn Not Available" - const val NO_ITEMS_IN_ADDON = "AddOn Items Not Found" const val ADDON_SEARCH_PLACEHOLDER = "Search for AddOn Items..." const val ADD_EDIT_ADDON_SCREEN = "Create New Addon Screen" - const val CREATE_ADD_ON = "Create AddOn" + + const val ADD_EDIT_ADDON_BUTTON = "AddEdit Addon" const val CREATE_NEW_ADD_ON = "Create New AddOn" const val EDIT_ADD_ON_ITEM = "Update AddOn Item" diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/domain/utils/AddressTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/AddressTestTags.kt similarity index 92% rename from app/src/main/java/com/niyaj/poposroom/features/address/domain/utils/AddressTestTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/AddressTestTags.kt index a706be59..2f998b4b 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/domain/utils/AddressTestTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/AddressTestTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.address.domain.utils +package com.niyaj.data.utils object AddressTestTags { @@ -10,7 +10,7 @@ object AddressTestTags { const val CREATE_ADDRESS_SCREEN = "Create New Address Screen" const val UPDATE_ADDRESS_SCREEN = "Update Address Screen" - const val CREATE_ADD_ON = "Create Address" + const val ADD_EDIT_ADDRESS_BTN = "AddEdit Address" const val CREATE_NEW_ADDRESS = "Create New Address" const val EDIT_ADDRESS = "Update Address" diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/utils/CartOrderTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/CartOrderTestTags.kt similarity index 97% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/utils/CartOrderTestTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/CartOrderTestTags.kt index a73d7353..d0455b31 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/utils/CartOrderTestTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/CartOrderTestTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart_order.domain.utils +package com.niyaj.data.utils object CartOrderTestTags { diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/domain/utils/CategoryConstants.kt b/core/data/src/main/java/com/niyaj/data/utils/CategoryConstants.kt similarity index 94% rename from app/src/main/java/com/niyaj/poposroom/features/category/domain/utils/CategoryConstants.kt rename to core/data/src/main/java/com/niyaj/data/utils/CategoryConstants.kt index 9907f60e..c5efce2e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/domain/utils/CategoryConstants.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/CategoryConstants.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.category.domain.utils +package com.niyaj.data.utils object CategoryConstants { diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/domain/utils/ChargesTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/ChargesTestTags.kt similarity index 93% rename from app/src/main/java/com/niyaj/poposroom/features/charges/domain/utils/ChargesTestTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/ChargesTestTags.kt index e40ac66a..a43d996c 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/domain/utils/ChargesTestTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/ChargesTestTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.charges.domain.utils +package com.niyaj.data.utils object ChargesTestTags { @@ -8,7 +8,6 @@ object ChargesTestTags { const val CHARGES_SEARCH_PLACEHOLDER = "Search for Charges Items..." const val ADD_EDIT_CHARGES_SCREEN = "Create New Charges Screen" - const val CREATE_CHARGES = "Create Charges" const val CREATE_NEW_CHARGES = "Create New Charges" const val EDIT_CHARGES_ITEM = "Update Charges Item" diff --git a/core/data/src/main/java/com/niyaj/data/utils/ConnectivityManagerNetworkMonitor.kt b/core/data/src/main/java/com/niyaj/data/utils/ConnectivityManagerNetworkMonitor.kt new file mode 100644 index 00000000..bad54cb7 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/utils/ConnectivityManagerNetworkMonitor.kt @@ -0,0 +1,67 @@ +package com.niyaj.data.utils + +import android.content.Context +import android.net.ConnectivityManager +import android.net.ConnectivityManager.NetworkCallback +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import android.net.NetworkRequest.Builder +import androidx.core.content.getSystemService +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.conflate +import javax.inject.Inject + +class ConnectivityManagerNetworkMonitor @Inject constructor( + @ApplicationContext private val context: Context, +) : NetworkMonitor { + override val isOnline: Flow = callbackFlow { + val connectivityManager = context.getSystemService() + if (connectivityManager == null) { + channel.trySend(false) + channel.close() + return@callbackFlow + } + + /** + * The callback's methods are invoked on changes to *any* network matching the [NetworkRequest], + * not just the active network. So we can simply track the presence (or absence) of such [Network]. + */ + val callback = object : NetworkCallback() { + + private val networks = mutableSetOf() + + override fun onAvailable(network: Network) { + networks += network + channel.trySend(true) + } + + override fun onLost(network: Network) { + networks -= network + channel.trySend(networks.isNotEmpty()) + } + } + + val request = Builder() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build() + connectivityManager.registerNetworkCallback(request, callback) + + /** + * Sends the latest connectivity status to the underlying channel. + */ + channel.trySend(connectivityManager.isCurrentlyConnected()) + + awaitClose { + connectivityManager.unregisterNetworkCallback(callback) + } + } + .conflate() + + private fun ConnectivityManager.isCurrentlyConnected() = activeNetwork + ?.let(::getNetworkCapabilities) + ?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false +} diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/domain/utils/CustomerTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/CustomerTestTags.kt similarity index 96% rename from app/src/main/java/com/niyaj/poposroom/features/customer/domain/utils/CustomerTestTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/CustomerTestTags.kt index 454ad618..4bd1d8b3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/domain/utils/CustomerTestTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/CustomerTestTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.customer.domain.utils +package com.niyaj.data.utils object CustomerTestTags { diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/utils/EmployeeTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/EmployeeTestTags.kt similarity index 97% rename from app/src/main/java/com/niyaj/poposroom/features/employee/domain/utils/EmployeeTestTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/EmployeeTestTags.kt index 190d5990..a23731d9 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/utils/EmployeeTestTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/EmployeeTestTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee.domain.utils +package com.niyaj.data.utils object EmployeeTestTags { diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/domain/utils/ExpenseTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/ExpenseTestTags.kt similarity index 95% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/domain/utils/ExpenseTestTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/ExpenseTestTags.kt index a12e0e4a..b3fbf73a 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/domain/utils/ExpenseTestTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/ExpenseTestTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.expenses.domain.utils +package com.niyaj.data.utils object ExpenseTestTags { diff --git a/app/src/main/java/com/niyaj/poposroom/features/main_feed/domain/utils/MainFeedTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/MainFeedTestTags.kt similarity index 88% rename from app/src/main/java/com/niyaj/poposroom/features/main_feed/domain/utils/MainFeedTestTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/MainFeedTestTags.kt index 636ecee0..0c231a8e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/main_feed/domain/utils/MainFeedTestTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/MainFeedTestTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.main_feed.domain.utils +package com.niyaj.data.utils object MainFeedTestTags { diff --git a/core/data/src/main/java/com/niyaj/data/utils/NetworkMonitor.kt b/core/data/src/main/java/com/niyaj/data/utils/NetworkMonitor.kt new file mode 100644 index 00000000..7d7b70f0 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/utils/NetworkMonitor.kt @@ -0,0 +1,10 @@ +package com.niyaj.data.utils + +import kotlinx.coroutines.flow.Flow + +/** + * Utility for reporting app connectivity status + */ +interface NetworkMonitor { + val isOnline: Flow +} diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/domain/utils/OrderTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/OrderTestTags.kt similarity index 85% rename from app/src/main/java/com/niyaj/poposroom/features/order/domain/utils/OrderTestTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/OrderTestTags.kt index bf41eca5..944c0e5b 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/domain/utils/OrderTestTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/OrderTestTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.order.domain.utils +package com.niyaj.data.utils object OrderTestTags { diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/utils/PaymentScreenTags.kt b/core/data/src/main/java/com/niyaj/data/utils/PaymentScreenTags.kt similarity index 96% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/utils/PaymentScreenTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/PaymentScreenTags.kt index 29cc461f..b2719a7f 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/utils/PaymentScreenTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/PaymentScreenTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_payment.domain.utils +package com.niyaj.data.utils object PaymentScreenTags { diff --git a/core/data/src/main/java/com/niyaj/data/utils/PrinterInfoTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/PrinterInfoTestTags.kt new file mode 100644 index 00000000..02be0199 --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/utils/PrinterInfoTestTags.kt @@ -0,0 +1,62 @@ +package com.niyaj.data.utils + +object PrinterInfoTestTags { + + const val PRINTER_SCREEN_TITLE = "Printer Information" + const val PRINTER_NOT_AVAIlABLE = "Printer Info Not Available" + + const val UPDATE_PRINTER_BUTTON = "Update Printer Button" + const val UPDATE_PRINTER_INFO = "Update Printer Info" + + const val PRINTER_INFO_NOTES_ONE = "First paired bluetooth printer will be connected automatically." + const val PRINTER_INFO_NOTES_TWO = "Do not worry about connection status, click on test print button to print test data." + const val PRINTER_INFO_NOTES_THREE = "If you did not uploaded the print logo then default logo will be used for printing." + const val PRINTER_INFO_NOTES_FOUR = "Print some order bill and check how it's look, if it's look weird then change printer info accordingly." + + + const val PRINTER_DPI_FIELD = "Printer DPI" + const val PRINTER_DPI_MESSAGE = "Usually found in the back side of the printer" + + const val PRINTER_WIDTH_FIELD = "Printer Width" + const val PRINTER_WIDTH_MESSAGE = "The printer width in millimeters(mm)" + + const val PRINTER_NBR_LINES_FIELD = "Printer NBR Lines" + const val PRINTER_NBR_LINES_MESSAGE = "How many characters should be printed in one line" + + const val PRINTER_PRODUCT_NAME_LENGTH_FIELD = "Product Name Length" + const val PRINTER_PRODUCT_NAME_LENGTH_MESSAGE = "Product name should be printed in one line" + + const val PRODUCT_REPORT_LIMIT_FIELD = "Product Report Limit" + const val PRODUCT_REPORT_LIMIT_MESSAGE = "How many products should be printed in a report" + + const val ADDRESS_REPORT_LIMIT_FIELD = "Address Report Limit" + const val ADDRESS_REPORT_LIMIT_MESSAGE = "How many addresses should be printed in a report" + + const val CUSTOMER_REPORT_LIMIT_FIELD = "Customer Report Limit" + const val CUSTOMER_REPORT_LIMIT_MESSAGE = "How many customers should be printed in a report" + + const val PRINT_QR_CODE_IN_BILL = "Print QR Code In Bill" + + const val PRINT_LOGO_IN_BILL = "Print Logo In Bill" + + const val PRINT_WELCOME_TEXT_IN_BILL = "Print Welcome Text In Bill" + + const val DPI_IS_REQUIRED = "Printer dpi is required." + + const val WIDTH_IS_REQUIRED = "Printer width is required." + + const val NBR_LINES_IS_REQUIRED = "Printer NBR line length is required." + + const val PRODUCT_NAME_LENGTH_IS_REQUIRED = "Product name length is required." + const val PRODUCT_NAME_LENGTH_IS_ERROR = "Product name length must be more than ten character." + + const val PRODUCT_REPORT_LIMIT_IS_REQUIRED = "Product report limit is required." + const val PRODUCT_REPORT_LENGTH_ERROR = "Product name limit must be more than 20." + + const val ADDRESS_REPORT_LIMIT_IS_REQUIRED = "Address report limit is required." + const val ADDRESS_REPORT_LENGTH_ERROR = "Address name limit must be more than 10." + + const val CUSTOM_REPORT_LIMIT_IS_REQUIRED = "Customer report limit is required." + const val CUSTOM_REPORT_LENGTH_ERROR = "Customer name limit must be more than 10." + +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/domain/utils/ProductTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/ProductTestTags.kt similarity index 96% rename from app/src/main/java/com/niyaj/poposroom/features/product/domain/utils/ProductTestTags.kt rename to core/data/src/main/java/com/niyaj/data/utils/ProductTestTags.kt index bcebd2b5..93ea9e29 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/domain/utils/ProductTestTags.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/ProductTestTags.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.domain.utils +package com.niyaj.data.utils object ProductTestTags { diff --git a/core/data/src/main/java/com/niyaj/data/utils/ProfileTestTags.kt b/core/data/src/main/java/com/niyaj/data/utils/ProfileTestTags.kt new file mode 100644 index 00000000..6076866c --- /dev/null +++ b/core/data/src/main/java/com/niyaj/data/utils/ProfileTestTags.kt @@ -0,0 +1,60 @@ +package com.niyaj.data.utils + +object ProfileTestTags { + + const val PROFILE_SCREEN = "Restaurant Info" + + const val PROFILE_NOT_AVAILABLE = "Restaurant info not available" + + const val ADD_EDIT_PROFILE_SCREEN = "Add/Edit Profile Screen" + const val ADD_EDIT_PROFILE_BTN = "Add/Edit profile info" + + const val CREATE_NEW_PROFILE = "Create New Profile" + + const val UPDATE_PROFILE = "Update Profile" + + const val NAME_FIELD = "Restaurant Name" + const val NAME_ERROR_FIELD = "Restaurant NameError" + const val NAME_EMPTY_ERROR = "Restaurant name cannot be empty." + const val NAME_LENGTH_ERROR = "Restaurant name must 5 characters long." + const val NAME_DIGITS_ERROR = "Restaurant name must not contain any digit." + + const val EMAIL_FIELD = "Restaurant Email" + const val EMAIL_ERROR_FIELD = "Restaurant EmailError" + const val EMAIL_EMPTY_ERROR = "Restaurant email cannot be empty." + const val EMAIL_NOT_VALID = "Restaurant email is not valid." + + const val P_PHONE_FIELD = "Restaurant Phone" + const val P_PHONE_ERROR_FIELD = "Restaurant PhoneError" + const val P_PHONE_EMPTY_ERROR = "Restaurant phone cannot be empty." + const val P_PHONE_CHAR_ERROR = "Restaurant phone must not contain any character." + const val P_PHONE_LENGTH_ERROR = "Restaurant phone must be 10 digits." + + + const val S_PHONE_FIELD = "Secondary Phone" + const val S_PHONE_ERROR_FIELD = "Secondary PhoneError" + const val S_PHONE_CHAR_ERROR = "Secondary phone must not contain any character." + const val S_PHONE_LENGTH_ERROR = "Secondary phone must be 10 digits." + + const val TAG_FIELD = "Restaurant Tagline" + const val TAG_ERROR_FIELD = "Restaurant TaglineError" + const val TAG_EMPTY_ERROR = "Restaurant tagline cannot be empty." + const val TAG_LENGTH_ERROR = "Restaurant tagline must be not more than 40 characters long." + + const val DESC_FIELD = "Restaurant Description" + const val DESC_ERROR_FIELD = "Restaurant DescriptionError" + const val DESC_EMPTY_ERROR = "Restaurant description cannot be empty." + const val DESC_LENGTH_ERROR = "Restaurant description must be not more than 120 characters long." + + const val ADDRESS_FIELD = "Restaurant Address" + const val ADDRESS_ERROR_FIELD = "Restaurant AddressError" + const val ADDRESS_EMPTY_ERROR = "Restaurant address cannot be empty." + + const val LOGO_FIELD = "Restaurant Logo" + const val LOGO_EMPTY_ERROR = "Restaurant logo must not be empty" + + const val PRINT_LOGO_FIELD = "Printing Logo" + + const val QR_CODE_FIELD = "Payment QR Data" + +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/selected/domain/utils/SelectedTestTag.kt b/core/data/src/main/java/com/niyaj/data/utils/SelectedTestTag.kt similarity index 60% rename from app/src/main/java/com/niyaj/poposroom/features/selected/domain/utils/SelectedTestTag.kt rename to core/data/src/main/java/com/niyaj/data/utils/SelectedTestTag.kt index 622e3cb2..4a84edc5 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/selected/domain/utils/SelectedTestTag.kt +++ b/core/data/src/main/java/com/niyaj/data/utils/SelectedTestTag.kt @@ -1,8 +1,6 @@ -package com.niyaj.poposroom.features.selected.domain.utils +package com.niyaj.data.utils object SelectedTestTag { - const val SELECTED_ID = "33333333" - const val SELECTED_SCREEN_TITLE = "Select Cart Order" const val SELECTED_SCREEN_NOTE = "Click an item to select." diff --git a/core/data/src/test/java/com/niyaj/data/ExampleUnitTest.kt b/core/data/src/test/java/com/niyaj/data/ExampleUnitTest.kt new file mode 100644 index 00000000..7a469b24 --- /dev/null +++ b/core/data/src/test/java/com/niyaj/data/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.data + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/core/database/.gitignore b/core/database/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/database/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts new file mode 100644 index 00000000..5ab72c9b --- /dev/null +++ b/core/database/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.library") + id("popos.android.library.jacoco") + id("popos.android.hilt") + id("popos.android.room") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.core.database" + + defaultConfig { + testInstrumentationRunner = "com.niyaj.testing.PoposTestRunner" + } + +} + +dependencies { + implementation(project(":core:model")) + implementation(project(":core:common")) + + implementation(libs.kotlinx.coroutines.android) + + androidTestImplementation(project(":core:testing")) + + //Moshi + ksp(libs.moshi.kotlin.codegen) + implementation(libs.moshi) +} \ No newline at end of file diff --git a/core/database/schemas/com.niyaj.database.PoposDatabase/1.json b/core/database/schemas/com.niyaj.database.PoposDatabase/1.json new file mode 100644 index 00000000..33f556a6 --- /dev/null +++ b/core/database/schemas/com.niyaj.database.PoposDatabase/1.json @@ -0,0 +1,76 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "4e78ee3ee1c1419da6c36e59bb39e9c9", + "entities": [ + { + "tableName": "addonitem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `itemName` TEXT NOT NULL, `itemPrice` INTEGER NOT NULL, `isApplicable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemName", + "columnName": "itemName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "itemPrice", + "columnName": "itemPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isApplicable", + "columnName": "isApplicable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_addonitem_itemId", + "unique": false, + "columnNames": [ + "itemId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_addonitem_itemId` ON `${TABLE_NAME}` (`itemId`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '4e78ee3ee1c1419da6c36e59bb39e9c9')" + ] + } +} \ No newline at end of file diff --git a/core/database/schemas/com.niyaj.database.PoposDatabase/2.json b/core/database/schemas/com.niyaj.database.PoposDatabase/2.json new file mode 100644 index 00000000..4618ad4d --- /dev/null +++ b/core/database/schemas/com.niyaj.database.PoposDatabase/2.json @@ -0,0 +1,1378 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "947a0372fb5bf8d49ba93a4ab8cfee93", + "entities": [ + { + "tableName": "addonitem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `itemName` TEXT NOT NULL, `itemPrice` INTEGER NOT NULL, `isApplicable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemName", + "columnName": "itemName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "itemPrice", + "columnName": "itemPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isApplicable", + "columnName": "isApplicable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_addonitem_itemId", + "unique": false, + "columnNames": [ + "itemId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_addonitem_itemId` ON `${TABLE_NAME}` (`itemId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "address", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`addressId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addressName` TEXT NOT NULL, `shortName` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "addressId", + "columnName": "addressId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "addressName", + "columnName": "addressName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "shortName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "addressId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "charges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`chargesId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `chargesName` TEXT NOT NULL, `chargesPrice` INTEGER NOT NULL, `isApplicable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "chargesId", + "columnName": "chargesId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chargesName", + "columnName": "chargesName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chargesPrice", + "columnName": "chargesPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isApplicable", + "columnName": "isApplicable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "chargesId" + ] + }, + "indices": [ + { + "name": "index_charges_chargesId", + "unique": false, + "columnNames": [ + "chargesId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_charges_chargesId` ON `${TABLE_NAME}` (`chargesId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "category", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `categoryName` TEXT NOT NULL, `isAvailable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryName", + "columnName": "categoryName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isAvailable", + "columnName": "isAvailable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "categoryId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "customer", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`customerId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `customerPhone` TEXT NOT NULL, `customerName` TEXT, `customerEmail` TEXT, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "customerId", + "columnName": "customerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customerPhone", + "columnName": "customerPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "customerName", + "columnName": "customerName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customerEmail", + "columnName": "customerEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "customerId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "employee", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`employeeId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `employeeName` TEXT NOT NULL, `employeePhone` TEXT NOT NULL, `employeeSalary` TEXT NOT NULL, `employeePosition` TEXT NOT NULL, `employeeJoinedDate` TEXT NOT NULL, `employeeEmail` TEXT, `employeeSalaryType` TEXT NOT NULL, `employeeType` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "employeeName", + "columnName": "employeeName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeePhone", + "columnName": "employeePhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeSalary", + "columnName": "employeeSalary", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeePosition", + "columnName": "employeePosition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeJoinedDate", + "columnName": "employeeJoinedDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeEmail", + "columnName": "employeeEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "employeeSalaryType", + "columnName": "employeeSalaryType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeType", + "columnName": "employeeType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "employeeId" + ] + }, + "indices": [ + { + "name": "index_employee_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_employee_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "payment", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`paymentId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `employeeId` INTEGER NOT NULL, `paymentAmount` TEXT NOT NULL, `paymentDate` TEXT NOT NULL, `paymentType` TEXT NOT NULL, `paymentMode` TEXT NOT NULL, `paymentNote` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL, FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "paymentId", + "columnName": "paymentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "paymentAmount", + "columnName": "paymentAmount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentDate", + "columnName": "paymentDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentType", + "columnName": "paymentType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentMode", + "columnName": "paymentMode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentNote", + "columnName": "paymentNote", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "paymentId" + ] + }, + "indices": [ + { + "name": "index_payment_paymentId", + "unique": false, + "columnNames": [ + "paymentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_payment_paymentId` ON `${TABLE_NAME}` (`paymentId`)" + }, + { + "name": "index_payment_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_payment_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + } + ] + }, + { + "tableName": "EmployeeWithPaymentCrossRef", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`employeeId` INTEGER NOT NULL, `paymentId` INTEGER NOT NULL, PRIMARY KEY(`employeeId`, `paymentId`), FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`paymentId`) REFERENCES `payment`(`paymentId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "paymentId", + "columnName": "paymentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "employeeId", + "paymentId" + ] + }, + "indices": [ + { + "name": "index_EmployeeWithPaymentCrossRef_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithPaymentCrossRef_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + }, + { + "name": "index_EmployeeWithPaymentCrossRef_paymentId", + "unique": false, + "columnNames": [ + "paymentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithPaymentCrossRef_paymentId` ON `${TABLE_NAME}` (`paymentId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + }, + { + "table": "payment", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "paymentId" + ], + "referencedColumns": [ + "paymentId" + ] + } + ] + }, + { + "tableName": "absent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`absentId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `employeeId` INTEGER NOT NULL, `absentReason` TEXT NOT NULL, `absentDate` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL, FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "absentId", + "columnName": "absentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absentReason", + "columnName": "absentReason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absentDate", + "columnName": "absentDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "absentId" + ] + }, + "indices": [ + { + "name": "index_absent_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_absent_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + } + ] + }, + { + "tableName": "EmployeeWithAbsentCrossRef", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`employeeId` INTEGER NOT NULL, `absentId` INTEGER NOT NULL, PRIMARY KEY(`employeeId`, `absentId`), FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`absentId`) REFERENCES `absent`(`absentId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absentId", + "columnName": "absentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "employeeId", + "absentId" + ] + }, + "indices": [ + { + "name": "index_EmployeeWithAbsentCrossRef_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithAbsentCrossRef_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + }, + { + "name": "index_EmployeeWithAbsentCrossRef_absentId", + "unique": false, + "columnNames": [ + "absentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithAbsentCrossRef_absentId` ON `${TABLE_NAME}` (`absentId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + }, + { + "table": "absent", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "absentId" + ], + "referencedColumns": [ + "absentId" + ] + } + ] + }, + { + "tableName": "expense", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`expenseId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `expenseName` TEXT NOT NULL, `expenseAmount` TEXT NOT NULL, `expenseDate` TEXT NOT NULL, `expenseNote` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "expenseId", + "columnName": "expenseId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expenseName", + "columnName": "expenseName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expenseAmount", + "columnName": "expenseAmount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expenseDate", + "columnName": "expenseDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expenseNote", + "columnName": "expenseNote", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "expenseId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "product", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`productId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `categoryId` INTEGER NOT NULL, `productName` TEXT NOT NULL, `productPrice` INTEGER NOT NULL, `productDescription` TEXT NOT NULL, `productAvailability` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL, FOREIGN KEY(`categoryId`) REFERENCES `category`(`categoryId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productName", + "columnName": "productName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "productPrice", + "columnName": "productPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productDescription", + "columnName": "productDescription", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "productAvailability", + "columnName": "productAvailability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "productId" + ] + }, + "indices": [ + { + "name": "index_product_categoryId", + "unique": false, + "columnNames": [ + "categoryId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_product_categoryId` ON `${TABLE_NAME}` (`categoryId`)" + } + ], + "foreignKeys": [ + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "categoryId" + ], + "referencedColumns": [ + "categoryId" + ] + } + ] + }, + { + "tableName": "CategoryWithProductCrossRef", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` INTEGER NOT NULL, `productId` INTEGER NOT NULL, PRIMARY KEY(`productId`, `categoryId`), FOREIGN KEY(`productId`) REFERENCES `product`(`productId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`categoryId`) REFERENCES `category`(`categoryId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "productId", + "categoryId" + ] + }, + "indices": [ + { + "name": "index_CategoryWithProductCrossRef_categoryId", + "unique": false, + "columnNames": [ + "categoryId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CategoryWithProductCrossRef_categoryId` ON `${TABLE_NAME}` (`categoryId`)" + }, + { + "name": "index_CategoryWithProductCrossRef_productId", + "unique": false, + "columnNames": [ + "productId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CategoryWithProductCrossRef_productId` ON `${TABLE_NAME}` (`productId`)" + } + ], + "foreignKeys": [ + { + "table": "product", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "productId" + ], + "referencedColumns": [ + "productId" + ] + }, + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "categoryId" + ], + "referencedColumns": [ + "categoryId" + ] + } + ] + }, + { + "tableName": "cartorder", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`orderId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `orderType` TEXT NOT NULL, `orderStatus` TEXT NOT NULL, `doesChargesIncluded` INTEGER NOT NULL, `addressId` INTEGER NOT NULL, `customerId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderType", + "columnName": "orderType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "orderStatus", + "columnName": "orderStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "doesChargesIncluded", + "columnName": "doesChargesIncluded", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "addressId", + "columnName": "addressId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customerId", + "columnName": "customerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "orderId" + ] + }, + "indices": [ + { + "name": "index_cartorder_addressId", + "unique": false, + "columnNames": [ + "addressId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cartorder_addressId` ON `${TABLE_NAME}` (`addressId`)" + }, + { + "name": "index_cartorder_customerId", + "unique": false, + "columnNames": [ + "customerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cartorder_customerId` ON `${TABLE_NAME}` (`customerId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "cart_addon_items", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`orderId` INTEGER NOT NULL, `itemId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`orderId`, `itemId`), FOREIGN KEY(`orderId`) REFERENCES `cartorder`(`orderId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`itemId`) REFERENCES `addonitem`(`itemId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "orderId", + "itemId" + ] + }, + "indices": [ + { + "name": "index_cart_addon_items_orderId", + "unique": false, + "columnNames": [ + "orderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_addon_items_orderId` ON `${TABLE_NAME}` (`orderId`)" + }, + { + "name": "index_cart_addon_items_itemId", + "unique": false, + "columnNames": [ + "itemId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_addon_items_itemId` ON `${TABLE_NAME}` (`itemId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "orderId" + ], + "referencedColumns": [ + "orderId" + ] + }, + { + "table": "addonitem", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "itemId" + ], + "referencedColumns": [ + "itemId" + ] + } + ] + }, + { + "tableName": "cart_charges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`orderId` INTEGER NOT NULL, `chargesId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`orderId`, `chargesId`), FOREIGN KEY(`orderId`) REFERENCES `cartorder`(`orderId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`chargesId`) REFERENCES `charges`(`chargesId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chargesId", + "columnName": "chargesId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "orderId", + "chargesId" + ] + }, + "indices": [ + { + "name": "index_cart_charges_orderId", + "unique": false, + "columnNames": [ + "orderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_charges_orderId` ON `${TABLE_NAME}` (`orderId`)" + }, + { + "name": "index_cart_charges_chargesId", + "unique": false, + "columnNames": [ + "chargesId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_charges_chargesId` ON `${TABLE_NAME}` (`chargesId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "orderId" + ], + "referencedColumns": [ + "orderId" + ] + }, + { + "table": "charges", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "chargesId" + ], + "referencedColumns": [ + "chargesId" + ] + } + ] + }, + { + "tableName": "selected", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`selectedId` TEXT NOT NULL, `orderId` INTEGER NOT NULL, PRIMARY KEY(`selectedId`), FOREIGN KEY(`orderId`) REFERENCES `cartorder`(`orderId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "selectedId", + "columnName": "selectedId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "selectedId" + ] + }, + "indices": [ + { + "name": "index_selected_selectedId", + "unique": false, + "columnNames": [ + "selectedId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_selected_selectedId` ON `${TABLE_NAME}` (`selectedId`)" + }, + { + "name": "index_selected_orderId", + "unique": false, + "columnNames": [ + "orderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_selected_orderId` ON `${TABLE_NAME}` (`orderId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "orderId" + ], + "referencedColumns": [ + "orderId" + ] + } + ] + }, + { + "tableName": "cart", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cartId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `orderId` INTEGER NOT NULL, `productId` INTEGER NOT NULL, `quantity` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(`orderId`) REFERENCES `cartorder`(`orderId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`productId`) REFERENCES `product`(`productId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "cartId", + "columnName": "cartId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "quantity", + "columnName": "quantity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "cartId" + ] + }, + "indices": [ + { + "name": "index_cart_orderId", + "unique": false, + "columnNames": [ + "orderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_orderId` ON `${TABLE_NAME}` (`orderId`)" + }, + { + "name": "index_cart_productId", + "unique": false, + "columnNames": [ + "productId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_productId` ON `${TABLE_NAME}` (`productId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "orderId" + ], + "referencedColumns": [ + "orderId" + ] + }, + { + "table": "product", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "productId" + ], + "referencedColumns": [ + "productId" + ] + } + ] + }, + { + "tableName": "profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`restaurantId` INTEGER NOT NULL, `name` TEXT NOT NULL, `email` TEXT NOT NULL, `primaryPhone` TEXT NOT NULL, `secondaryPhone` TEXT NOT NULL, `tagline` TEXT NOT NULL, `description` TEXT NOT NULL, `address` TEXT NOT NULL, `logo` TEXT NOT NULL, `printLogo` TEXT NOT NULL, `paymentQrCode` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT, PRIMARY KEY(`restaurantId`))", + "fields": [ + { + "fieldPath": "restaurantId", + "columnName": "restaurantId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "primaryPhone", + "columnName": "primaryPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondaryPhone", + "columnName": "secondaryPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tagline", + "columnName": "tagline", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "logo", + "columnName": "logo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "printLogo", + "columnName": "printLogo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentQrCode", + "columnName": "paymentQrCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "restaurantId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '947a0372fb5bf8d49ba93a4ab8cfee93')" + ] + } +} \ No newline at end of file diff --git a/core/database/schemas/com.niyaj.database.PoposDatabase/3.json b/core/database/schemas/com.niyaj.database.PoposDatabase/3.json new file mode 100644 index 00000000..8dd096fd --- /dev/null +++ b/core/database/schemas/com.niyaj.database.PoposDatabase/3.json @@ -0,0 +1,1480 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "ca16b8f0e0d0abce8e5f2f67b6b05f67", + "entities": [ + { + "tableName": "addonitem", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `itemName` TEXT NOT NULL, `itemPrice` INTEGER NOT NULL, `isApplicable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemName", + "columnName": "itemName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "itemPrice", + "columnName": "itemPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isApplicable", + "columnName": "isApplicable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_addonitem_itemId", + "unique": false, + "columnNames": [ + "itemId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_addonitem_itemId` ON `${TABLE_NAME}` (`itemId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "address", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`addressId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addressName` TEXT NOT NULL, `shortName` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "addressId", + "columnName": "addressId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "addressName", + "columnName": "addressName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "shortName", + "columnName": "shortName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "addressId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "charges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`chargesId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `chargesName` TEXT NOT NULL, `chargesPrice` INTEGER NOT NULL, `isApplicable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "chargesId", + "columnName": "chargesId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chargesName", + "columnName": "chargesName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chargesPrice", + "columnName": "chargesPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isApplicable", + "columnName": "isApplicable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "chargesId" + ] + }, + "indices": [ + { + "name": "index_charges_chargesId", + "unique": false, + "columnNames": [ + "chargesId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_charges_chargesId` ON `${TABLE_NAME}` (`chargesId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "category", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `categoryName` TEXT NOT NULL, `isAvailable` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryName", + "columnName": "categoryName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isAvailable", + "columnName": "isAvailable", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "categoryId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "customer", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`customerId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `customerPhone` TEXT NOT NULL, `customerName` TEXT, `customerEmail` TEXT, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "customerId", + "columnName": "customerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customerPhone", + "columnName": "customerPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "customerName", + "columnName": "customerName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customerEmail", + "columnName": "customerEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "customerId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "employee", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`employeeId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `employeeName` TEXT NOT NULL, `employeePhone` TEXT NOT NULL, `employeeSalary` TEXT NOT NULL, `employeePosition` TEXT NOT NULL, `employeeJoinedDate` TEXT NOT NULL, `employeeEmail` TEXT, `employeeSalaryType` TEXT NOT NULL, `employeeType` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "employeeName", + "columnName": "employeeName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeePhone", + "columnName": "employeePhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeSalary", + "columnName": "employeeSalary", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeePosition", + "columnName": "employeePosition", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeJoinedDate", + "columnName": "employeeJoinedDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeEmail", + "columnName": "employeeEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "employeeSalaryType", + "columnName": "employeeSalaryType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "employeeType", + "columnName": "employeeType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "employeeId" + ] + }, + "indices": [ + { + "name": "index_employee_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_employee_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "payment", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`paymentId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `employeeId` INTEGER NOT NULL, `paymentAmount` TEXT NOT NULL, `paymentDate` TEXT NOT NULL, `paymentType` TEXT NOT NULL, `paymentMode` TEXT NOT NULL, `paymentNote` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL, FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "paymentId", + "columnName": "paymentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "paymentAmount", + "columnName": "paymentAmount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentDate", + "columnName": "paymentDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentType", + "columnName": "paymentType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentMode", + "columnName": "paymentMode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentNote", + "columnName": "paymentNote", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "paymentId" + ] + }, + "indices": [ + { + "name": "index_payment_paymentId", + "unique": false, + "columnNames": [ + "paymentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_payment_paymentId` ON `${TABLE_NAME}` (`paymentId`)" + }, + { + "name": "index_payment_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_payment_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + } + ] + }, + { + "tableName": "EmployeeWithPaymentCrossRef", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`employeeId` INTEGER NOT NULL, `paymentId` INTEGER NOT NULL, PRIMARY KEY(`employeeId`, `paymentId`), FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`paymentId`) REFERENCES `payment`(`paymentId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "paymentId", + "columnName": "paymentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "employeeId", + "paymentId" + ] + }, + "indices": [ + { + "name": "index_EmployeeWithPaymentCrossRef_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithPaymentCrossRef_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + }, + { + "name": "index_EmployeeWithPaymentCrossRef_paymentId", + "unique": false, + "columnNames": [ + "paymentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithPaymentCrossRef_paymentId` ON `${TABLE_NAME}` (`paymentId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + }, + { + "table": "payment", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "paymentId" + ], + "referencedColumns": [ + "paymentId" + ] + } + ] + }, + { + "tableName": "absent", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`absentId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `employeeId` INTEGER NOT NULL, `absentReason` TEXT NOT NULL, `absentDate` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL, FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "absentId", + "columnName": "absentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absentReason", + "columnName": "absentReason", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "absentDate", + "columnName": "absentDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "absentId" + ] + }, + "indices": [ + { + "name": "index_absent_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_absent_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + } + ] + }, + { + "tableName": "EmployeeWithAbsentCrossRef", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`employeeId` INTEGER NOT NULL, `absentId` INTEGER NOT NULL, PRIMARY KEY(`employeeId`, `absentId`), FOREIGN KEY(`employeeId`) REFERENCES `employee`(`employeeId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`absentId`) REFERENCES `absent`(`absentId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "employeeId", + "columnName": "employeeId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "absentId", + "columnName": "absentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "employeeId", + "absentId" + ] + }, + "indices": [ + { + "name": "index_EmployeeWithAbsentCrossRef_employeeId", + "unique": false, + "columnNames": [ + "employeeId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithAbsentCrossRef_employeeId` ON `${TABLE_NAME}` (`employeeId`)" + }, + { + "name": "index_EmployeeWithAbsentCrossRef_absentId", + "unique": false, + "columnNames": [ + "absentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_EmployeeWithAbsentCrossRef_absentId` ON `${TABLE_NAME}` (`absentId`)" + } + ], + "foreignKeys": [ + { + "table": "employee", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "employeeId" + ], + "referencedColumns": [ + "employeeId" + ] + }, + { + "table": "absent", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "absentId" + ], + "referencedColumns": [ + "absentId" + ] + } + ] + }, + { + "tableName": "expense", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`expenseId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `expenseName` TEXT NOT NULL, `expenseAmount` TEXT NOT NULL, `expenseDate` TEXT NOT NULL, `expenseNote` TEXT NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "expenseId", + "columnName": "expenseId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expenseName", + "columnName": "expenseName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expenseAmount", + "columnName": "expenseAmount", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expenseDate", + "columnName": "expenseDate", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expenseNote", + "columnName": "expenseNote", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "expenseId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "product", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`productId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `categoryId` INTEGER NOT NULL, `productName` TEXT NOT NULL, `productPrice` INTEGER NOT NULL, `productDescription` TEXT NOT NULL, `productAvailability` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL, FOREIGN KEY(`categoryId`) REFERENCES `category`(`categoryId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productName", + "columnName": "productName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "productPrice", + "columnName": "productPrice", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productDescription", + "columnName": "productDescription", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "productAvailability", + "columnName": "productAvailability", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "productId" + ] + }, + "indices": [ + { + "name": "index_product_categoryId", + "unique": false, + "columnNames": [ + "categoryId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_product_categoryId` ON `${TABLE_NAME}` (`categoryId`)" + } + ], + "foreignKeys": [ + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "categoryId" + ], + "referencedColumns": [ + "categoryId" + ] + } + ] + }, + { + "tableName": "CategoryWithProductCrossRef", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`categoryId` INTEGER NOT NULL, `productId` INTEGER NOT NULL, PRIMARY KEY(`productId`, `categoryId`), FOREIGN KEY(`productId`) REFERENCES `product`(`productId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`categoryId`) REFERENCES `category`(`categoryId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "categoryId", + "columnName": "categoryId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "productId", + "categoryId" + ] + }, + "indices": [ + { + "name": "index_CategoryWithProductCrossRef_categoryId", + "unique": false, + "columnNames": [ + "categoryId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CategoryWithProductCrossRef_categoryId` ON `${TABLE_NAME}` (`categoryId`)" + }, + { + "name": "index_CategoryWithProductCrossRef_productId", + "unique": false, + "columnNames": [ + "productId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_CategoryWithProductCrossRef_productId` ON `${TABLE_NAME}` (`productId`)" + } + ], + "foreignKeys": [ + { + "table": "product", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "productId" + ], + "referencedColumns": [ + "productId" + ] + }, + { + "table": "category", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "categoryId" + ], + "referencedColumns": [ + "categoryId" + ] + } + ] + }, + { + "tableName": "cartorder", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`orderId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `orderType` TEXT NOT NULL, `orderStatus` TEXT NOT NULL, `doesChargesIncluded` INTEGER NOT NULL, `addressId` INTEGER NOT NULL, `customerId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT NULL)", + "fields": [ + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderType", + "columnName": "orderType", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "orderStatus", + "columnName": "orderStatus", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "doesChargesIncluded", + "columnName": "doesChargesIncluded", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "addressId", + "columnName": "addressId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customerId", + "columnName": "customerId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "NULL" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "orderId" + ] + }, + "indices": [ + { + "name": "index_cartorder_addressId", + "unique": false, + "columnNames": [ + "addressId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cartorder_addressId` ON `${TABLE_NAME}` (`addressId`)" + }, + { + "name": "index_cartorder_customerId", + "unique": false, + "columnNames": [ + "customerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cartorder_customerId` ON `${TABLE_NAME}` (`customerId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "cart_addon_items", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`orderId` INTEGER NOT NULL, `itemId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`orderId`, `itemId`), FOREIGN KEY(`orderId`) REFERENCES `cartorder`(`orderId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`itemId`) REFERENCES `addonitem`(`itemId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "orderId", + "itemId" + ] + }, + "indices": [ + { + "name": "index_cart_addon_items_orderId", + "unique": false, + "columnNames": [ + "orderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_addon_items_orderId` ON `${TABLE_NAME}` (`orderId`)" + }, + { + "name": "index_cart_addon_items_itemId", + "unique": false, + "columnNames": [ + "itemId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_addon_items_itemId` ON `${TABLE_NAME}` (`itemId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "orderId" + ], + "referencedColumns": [ + "orderId" + ] + }, + { + "table": "addonitem", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "itemId" + ], + "referencedColumns": [ + "itemId" + ] + } + ] + }, + { + "tableName": "cart_charges", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`orderId` INTEGER NOT NULL, `chargesId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(`orderId`, `chargesId`), FOREIGN KEY(`orderId`) REFERENCES `cartorder`(`orderId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`chargesId`) REFERENCES `charges`(`chargesId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chargesId", + "columnName": "chargesId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "orderId", + "chargesId" + ] + }, + "indices": [ + { + "name": "index_cart_charges_orderId", + "unique": false, + "columnNames": [ + "orderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_charges_orderId` ON `${TABLE_NAME}` (`orderId`)" + }, + { + "name": "index_cart_charges_chargesId", + "unique": false, + "columnNames": [ + "chargesId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_charges_chargesId` ON `${TABLE_NAME}` (`chargesId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "orderId" + ], + "referencedColumns": [ + "orderId" + ] + }, + { + "table": "charges", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "chargesId" + ], + "referencedColumns": [ + "chargesId" + ] + } + ] + }, + { + "tableName": "selected", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`selectedId` TEXT NOT NULL, `orderId` INTEGER NOT NULL, PRIMARY KEY(`selectedId`), FOREIGN KEY(`orderId`) REFERENCES `cartorder`(`orderId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "selectedId", + "columnName": "selectedId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "selectedId" + ] + }, + "indices": [ + { + "name": "index_selected_selectedId", + "unique": false, + "columnNames": [ + "selectedId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_selected_selectedId` ON `${TABLE_NAME}` (`selectedId`)" + }, + { + "name": "index_selected_orderId", + "unique": false, + "columnNames": [ + "orderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_selected_orderId` ON `${TABLE_NAME}` (`orderId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "orderId" + ], + "referencedColumns": [ + "orderId" + ] + } + ] + }, + { + "tableName": "cart", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`cartId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `orderId` INTEGER NOT NULL, `productId` INTEGER NOT NULL, `quantity` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatedAt` INTEGER DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(`orderId`) REFERENCES `cartorder`(`orderId`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`productId`) REFERENCES `product`(`productId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "cartId", + "columnName": "cartId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "orderId", + "columnName": "orderId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productId", + "columnName": "productId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "quantity", + "columnName": "quantity", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "CURRENT_TIMESTAMP" + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "CURRENT_TIMESTAMP" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "cartId" + ] + }, + "indices": [ + { + "name": "index_cart_orderId", + "unique": false, + "columnNames": [ + "orderId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_orderId` ON `${TABLE_NAME}` (`orderId`)" + }, + { + "name": "index_cart_productId", + "unique": false, + "columnNames": [ + "productId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_cart_productId` ON `${TABLE_NAME}` (`productId`)" + } + ], + "foreignKeys": [ + { + "table": "cartorder", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "orderId" + ], + "referencedColumns": [ + "orderId" + ] + }, + { + "table": "product", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "productId" + ], + "referencedColumns": [ + "productId" + ] + } + ] + }, + { + "tableName": "profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`restaurantId` INTEGER NOT NULL, `name` TEXT NOT NULL, `email` TEXT NOT NULL, `primaryPhone` TEXT NOT NULL, `secondaryPhone` TEXT NOT NULL, `tagline` TEXT NOT NULL, `description` TEXT NOT NULL, `address` TEXT NOT NULL, `logo` TEXT NOT NULL, `printLogo` TEXT NOT NULL, `paymentQrCode` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT, PRIMARY KEY(`restaurantId`))", + "fields": [ + { + "fieldPath": "restaurantId", + "columnName": "restaurantId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "primaryPhone", + "columnName": "primaryPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "secondaryPhone", + "columnName": "secondaryPhone", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tagline", + "columnName": "tagline", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "logo", + "columnName": "logo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "printLogo", + "columnName": "printLogo", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentQrCode", + "columnName": "paymentQrCode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "restaurantId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "printerInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`printerId` TEXT NOT NULL, `printerDpi` INTEGER NOT NULL, `printerWidth` REAL NOT NULL, `printerNbrLines` INTEGER NOT NULL, `productNameLength` INTEGER NOT NULL, `productWiseReportLimit` INTEGER NOT NULL, `addressWiseReportLimit` INTEGER NOT NULL, `customerWiseReportLimit` INTEGER NOT NULL, `printQRCode` INTEGER NOT NULL, `printResLogo` INTEGER NOT NULL, `printWelcomeText` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `updatedAt` TEXT, PRIMARY KEY(`printerId`))", + "fields": [ + { + "fieldPath": "printerId", + "columnName": "printerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "printerDpi", + "columnName": "printerDpi", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "printerWidth", + "columnName": "printerWidth", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "printerNbrLines", + "columnName": "printerNbrLines", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productNameLength", + "columnName": "productNameLength", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "productWiseReportLimit", + "columnName": "productWiseReportLimit", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "addressWiseReportLimit", + "columnName": "addressWiseReportLimit", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customerWiseReportLimit", + "columnName": "customerWiseReportLimit", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "printQRCode", + "columnName": "printQRCode", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "printResLogo", + "columnName": "printResLogo", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "printWelcomeText", + "columnName": "printWelcomeText", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "printerId" + ] + }, + "indices": [ + { + "name": "index_printerInfo_printerId", + "unique": false, + "columnNames": [ + "printerId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_printerInfo_printerId` ON `${TABLE_NAME}` (`printerId`)" + } + ], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ca16b8f0e0d0abce8e5f2f67b6b05f67')" + ] + } +} \ No newline at end of file diff --git a/core/database/src/androidTest/java/com/niyaj/database/ExampleInstrumentedTest.kt b/core/database/src/androidTest/java/com/niyaj/database/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..ce388ebc --- /dev/null +++ b/core/database/src/androidTest/java/com/niyaj/database/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.database + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.database.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/database/src/main/AndroidManifest.xml b/core/database/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/database/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/DaosModule.kt b/core/database/src/main/java/com/niyaj/database/DaosModule.kt new file mode 100644 index 00000000..8eb22eed --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/DaosModule.kt @@ -0,0 +1,83 @@ +package com.niyaj.database + +import com.niyaj.database.dao.AbsentDao +import com.niyaj.database.dao.AddOnItemDao +import com.niyaj.database.dao.AddressDao +import com.niyaj.database.dao.CartDao +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.CategoryDao +import com.niyaj.database.dao.CustomerDao +import com.niyaj.database.dao.EmployeeDao +import com.niyaj.database.dao.ExpenseDao +import com.niyaj.database.dao.MainFeedDao +import com.niyaj.database.dao.OrderDao +import com.niyaj.database.dao.PrintDao +import com.niyaj.database.dao.PrinterDao +import com.niyaj.database.dao.ProductDao +import com.niyaj.database.dao.ProfileDao +import com.niyaj.database.dao.SelectedDao +import com.niyaj.poposroom.features.charges.data.dao.ChargesDao +import com.niyaj.poposroom.features.employee_payment.data.dao.PaymentDao +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@Module +@InstallIn(SingletonComponent::class) +object DaosModule { + + @Provides + fun providesAbsentDao(database: PoposDatabase): AbsentDao = database.absentDao() + + @Provides + fun providesAddOnItemDao(database: PoposDatabase): AddOnItemDao = database.addOnItemDao() + + @Provides + fun providesAddressDao(database: PoposDatabase): AddressDao = database.addressDao() + + @Provides + fun providesCartDao(database: PoposDatabase): CartDao = database.cartDao() + + @Provides + fun providesCartOrderDao(database: PoposDatabase): CartOrderDao = database.cartOrderDao() + + @Provides + fun providesCategoryDao(database: PoposDatabase): CategoryDao = database.categoryDao() + + @Provides + fun providesChargesDao(database: PoposDatabase): ChargesDao = database.chargesDao() + + @Provides + fun providesCustomerDao(database: PoposDatabase): CustomerDao = database.customerDao() + + @Provides + fun providesEmployeeDao(database: PoposDatabase): EmployeeDao = database.employeeDao() + + @Provides + fun providesExpenseDao(database: PoposDatabase): ExpenseDao = database.expenseDao() + + @Provides + fun providesMainFeedDao(database: PoposDatabase): MainFeedDao = database.mainFeedDao() + + @Provides + fun providesOrderDao(database: PoposDatabase): OrderDao = database.orderDao() + + @Provides + fun providesPaymentDao(database: PoposDatabase): PaymentDao = database.paymentDao() + + @Provides + fun providesPrintDao(database: PoposDatabase): PrintDao = database.printDao() + + @Provides + fun providesPrinterDao(database: PoposDatabase): PrinterDao = database.printerDao() + + @Provides + fun providesProductDao(database: PoposDatabase): ProductDao = database.productDao() + + @Provides + fun provideProfileDao(database: PoposDatabase): ProfileDao = database.profileDao() + + @Provides + fun provideSelectedDao(database: PoposDatabase): SelectedDao = database.selectedDao() +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/database/di/DatabaseModule.kt b/core/database/src/main/java/com/niyaj/database/DatabaseModule.kt similarity index 83% rename from app/src/main/java/com/niyaj/poposroom/features/common/database/di/DatabaseModule.kt rename to core/database/src/main/java/com/niyaj/database/DatabaseModule.kt index 26e8b15d..423b32e3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/database/di/DatabaseModule.kt +++ b/core/database/src/main/java/com/niyaj/database/DatabaseModule.kt @@ -1,8 +1,7 @@ -package com.niyaj.poposroom.features.common.database.di +package com.niyaj.database import android.content.Context import androidx.room.Room -import com.niyaj.poposroom.features.common.database.PoposDatabase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -13,6 +12,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object DatabaseModule { + @Provides @Singleton fun providesNiaDatabase( diff --git a/core/database/src/main/java/com/niyaj/database/PoposDatabase.kt b/core/database/src/main/java/com/niyaj/database/PoposDatabase.kt new file mode 100644 index 00000000..9c50ae05 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/PoposDatabase.kt @@ -0,0 +1,94 @@ +package com.niyaj.database + +import androidx.room.Database +import androidx.room.RoomDatabase +import androidx.room.TypeConverters +import com.niyaj.database.dao.AbsentDao +import com.niyaj.database.dao.AddOnItemDao +import com.niyaj.database.dao.AddressDao +import com.niyaj.database.dao.CartDao +import com.niyaj.database.dao.CartOrderDao +import com.niyaj.database.dao.CategoryDao +import com.niyaj.database.dao.CustomerDao +import com.niyaj.database.dao.EmployeeDao +import com.niyaj.database.dao.ExpenseDao +import com.niyaj.database.dao.MainFeedDao +import com.niyaj.database.dao.OrderDao +import com.niyaj.database.dao.PrintDao +import com.niyaj.database.dao.PrinterDao +import com.niyaj.database.dao.ProductDao +import com.niyaj.database.dao.ProfileDao +import com.niyaj.database.dao.SelectedDao +import com.niyaj.database.model.AbsentEntity +import com.niyaj.database.model.AddOnItemEntity +import com.niyaj.database.model.AddressEntity +import com.niyaj.database.model.CartAddOnItemsEntity +import com.niyaj.database.model.CartChargesEntity +import com.niyaj.database.model.CartEntity +import com.niyaj.database.model.CartOrderEntity +import com.niyaj.database.model.CategoryEntity +import com.niyaj.database.model.CategoryWithProductCrossRef +import com.niyaj.database.model.ChargesEntity +import com.niyaj.database.model.CustomerEntity +import com.niyaj.database.model.EmployeeEntity +import com.niyaj.database.model.EmployeeWithAbsentCrossRef +import com.niyaj.database.model.EmployeeWithPaymentCrossRef +import com.niyaj.database.model.ExpenseEntity +import com.niyaj.database.model.PaymentEntity +import com.niyaj.database.model.PrinterEntity +import com.niyaj.database.model.ProductEntity +import com.niyaj.database.model.ProfileEntity +import com.niyaj.database.model.SelectedEntity +import com.niyaj.database.util.TimestampConverters +import com.niyaj.poposroom.features.charges.data.dao.ChargesDao +import com.niyaj.poposroom.features.employee_payment.data.dao.PaymentDao + + +@Database( + entities = [ + AddOnItemEntity::class, + AddressEntity::class, + ChargesEntity::class, + CategoryEntity::class, + CustomerEntity::class, + EmployeeEntity::class, + PaymentEntity::class, + EmployeeWithPaymentCrossRef::class, + AbsentEntity::class, + EmployeeWithAbsentCrossRef::class, + ExpenseEntity::class, + ProductEntity::class, + CategoryWithProductCrossRef::class, + CartOrderEntity::class, + CartAddOnItemsEntity::class, + CartChargesEntity::class, + SelectedEntity::class, + CartEntity::class, + ProfileEntity::class, + PrinterEntity::class, + ], + version = 3, + autoMigrations = [], + exportSchema = true, +) +@TypeConverters(TimestampConverters::class) +abstract class PoposDatabase : RoomDatabase() { + abstract fun addOnItemDao(): AddOnItemDao + abstract fun addressDao(): AddressDao + abstract fun chargesDao(): ChargesDao + abstract fun categoryDao(): CategoryDao + abstract fun customerDao(): CustomerDao + abstract fun employeeDao(): EmployeeDao + abstract fun paymentDao(): PaymentDao + abstract fun absentDao(): AbsentDao + abstract fun expenseDao(): ExpenseDao + abstract fun productDao(): ProductDao + abstract fun cartOrderDao(): CartOrderDao + abstract fun selectedDao(): SelectedDao + abstract fun mainFeedDao(): MainFeedDao + abstract fun cartDao(): CartDao + abstract fun orderDao(): OrderDao + abstract fun printDao(): PrintDao + abstract fun printerDao(): PrinterDao + abstract fun profileDao(): ProfileDao +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/data/dao/AbsentDao.kt b/core/database/src/main/java/com/niyaj/database/dao/AbsentDao.kt similarity index 62% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/data/dao/AbsentDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/AbsentDao.kt index c1d3ca9c..8a1221a7 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/data/dao/AbsentDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/AbsentDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_absent.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -7,10 +7,10 @@ import androidx.room.Query import androidx.room.Transaction import androidx.room.Update import androidx.room.Upsert -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee_absent.domain.model.Absent -import com.niyaj.poposroom.features.employee_absent.domain.model.EmployeeWithAbsent -import com.niyaj.poposroom.features.employee_absent.domain.model.EmployeeWithAbsentCrossRef +import com.niyaj.database.model.AbsentEntity +import com.niyaj.database.model.EmployeeEntity +import com.niyaj.database.model.EmployeeWithAbsentCrossRef +import com.niyaj.database.model.EmployeeWithAbsentsDto import kotlinx.coroutines.flow.Flow @Dao @@ -20,46 +20,46 @@ interface AbsentDao { @Query(value = """ SELECT * FROM employee """) - fun getAllAbsentEmployee(): Flow> + fun getAllAbsentEmployee(): Flow> @Query(value = """ SELECT * FROM employee """) - fun getAllEmployee(): Flow> + fun getAllEmployee(): Flow> @Query(value = """ SELECT * FROM employee WHERE employeeId = :employeeId """ ) - suspend fun getEmployeeById(employeeId: Int): Employee? + suspend fun getEmployeeById(employeeId: Int): EmployeeEntity? @Query(value = """ SELECT * FROM absent ORDER BY createdAt DESC """) - fun getAllAbsent(): Flow> + fun getAllAbsent(): Flow> @Query(value = """ SELECT * FROM absent WHERE absentId = :absentId """) - suspend fun getAbsentById(absentId: Int): Absent? + suspend fun getAbsentById(absentId: Int): AbsentEntity? /** - * Inserts [Absent] into the db if they don't exist, and ignores those that do + * Inserts [AbsentEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreAbsent(absent: Absent): Long + suspend fun insertOrIgnoreAbsent(absent: AbsentEntity): Long /** - * Updates [Absent] in the db that match the primary key, and no-ops if they don't + * Updates [AbsentEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updateAbsent(absent: Absent): Int + suspend fun updateAbsent(absent: AbsentEntity): Int /** - * Inserts or updates [Absent] in the db under the specified primary keys + * Inserts or updates [AbsentEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertAbsent(absent: Absent): Long + suspend fun upsertAbsent(absent: AbsentEntity): Long @Insert(entity = EmployeeWithAbsentCrossRef::class, onConflict = OnConflictStrategy.REPLACE) suspend fun upsertEmployeeWithAbsentCrossReference(employeeWithAbsent: EmployeeWithAbsentCrossRef) @@ -87,5 +87,5 @@ interface AbsentDao { ELSE absentId != :absentId AND absentDate = :absentDate AND employeeId = :employeeId END LIMIT 1 """) - fun findEmployeeByDate(absentDate: String, employeeId: Int, absentId: Int?,): Absent? + fun findEmployeeByDate(absentDate: String, employeeId: Int, absentId: Int?,): AbsentEntity? } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/data/dao/AddOnItemDao.kt b/core/database/src/main/java/com/niyaj/database/dao/AddOnItemDao.kt similarity index 64% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/data/dao/AddOnItemDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/AddOnItemDao.kt index 7197799f..3d26d930 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/data/dao/AddOnItemDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/AddOnItemDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.addon_item.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -6,7 +6,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update import androidx.room.Upsert -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem +import com.niyaj.database.model.AddOnItemEntity import kotlinx.coroutines.flow.Flow @Dao @@ -15,30 +15,30 @@ interface AddOnItemDao { @Query(value = """ SELECT * FROM addonitem ORDER BY createdAt DESC """) - fun getAllAddOnItems(): Flow> + fun getAllAddOnItems(): Flow> @Query(value = """ SELECT * FROM addonitem WHERE itemId = :itemId """) - fun getAddOnItemById(itemId: Int): AddOnItem? + fun getAddOnItemById(itemId: Int): AddOnItemEntity? /** - * Inserts [AddOnItem] into the db if they don't exist, and ignores those that do + * Inserts [AddOnItemEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreAddOnItem(addOnItem: AddOnItem): Long + suspend fun insertOrIgnoreAddOnItem(addOnItem: AddOnItemEntity): Long /** - * Updates [AddOnItem] in the db that match the primary key, and no-ops if they don't + * Updates [AddOnItemEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updateAddOnItem(addOnItem: AddOnItem): Int + suspend fun updateAddOnItem(addOnItem: AddOnItemEntity): Int /** - * Inserts or updates [AddOnItem] in the db under the specified primary keys + * Inserts or updates [AddOnItemEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertAddOnItem(addOnItem: AddOnItem): Long + suspend fun upsertAddOnItem(addOnItem: AddOnItemEntity): Long @Query(value = """ DELETE FROM addonitem WHERE itemId = :itemId @@ -63,5 +63,5 @@ interface AddOnItemDao { ELSE itemId != :itemId AND itemName = :itemName END LIMIT 1 """) - fun findAddOnItemByName(itemName: String, itemId: Int?): AddOnItem? + fun findAddOnItemByName(itemName: String, itemId: Int?): AddOnItemEntity? } diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/data/dao/AddressDao.kt b/core/database/src/main/java/com/niyaj/database/dao/AddressDao.kt similarity index 69% rename from app/src/main/java/com/niyaj/poposroom/features/address/data/dao/AddressDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/AddressDao.kt index b18885d4..5d71e29c 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/data/dao/AddressDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/AddressDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.address.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -6,7 +6,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update import androidx.room.Upsert -import com.niyaj.poposroom.features.address.domain.model.Address +import com.niyaj.database.model.AddressEntity import kotlinx.coroutines.flow.Flow @Dao @@ -15,12 +15,12 @@ interface AddressDao { @Query(value = """ SELECT * FROM address ORDER BY createdAt DESC """) - fun getAllAddresses(): Flow> + fun getAllAddresses(): Flow> @Query(value = """ SELECT * FROM address WHERE addressId = :addressId """) - fun getAddressById(addressId: Int): Address? + fun getAddressById(addressId: Int): AddressEntity? /** * Get addressId from database by [addressName] @@ -32,22 +32,22 @@ interface AddressDao { suspend fun getAddressByName(addressName: String): Int? /** - * Inserts [Address] into the db if they don't exist, and ignores those that do + * Inserts [AddressEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreAddress(address: Address): Long + suspend fun insertOrIgnoreAddress(address: AddressEntity): Long /** - * Updates [Address] in the db that match the primary key, and no-ops if they don't + * Updates [AddressEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updateAddress(address: Address): Int + suspend fun updateAddress(address: AddressEntity): Int /** - * Inserts or updates [Address] in the db under the specified primary keys + * Inserts or updates [AddressEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertAddress(address: Address): Long + suspend fun upsertAddress(address: AddressEntity): Long @Query(value = """ DELETE FROM address WHERE addressId = :addressId @@ -72,5 +72,5 @@ interface AddressDao { ELSE addressId != :addressId AND addressName = :addressName END LIMIT 1 """) - fun findAddressByName(addressName: String, addressId: Int?): Address? + fun findAddressByName(addressName: String, addressId: Int?): AddressEntity? } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/data/dao/CartDao.kt b/core/database/src/main/java/com/niyaj/database/dao/CartDao.kt similarity index 78% rename from app/src/main/java/com/niyaj/poposroom/features/cart/data/dao/CartDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/CartDao.kt index a8755eca..84e952e6 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/data/dao/CartDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/CartDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -6,15 +6,15 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction import androidx.room.Upsert -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnPriceWithApplicable -import com.niyaj.poposroom.features.cart.domain.model.CartEntity -import com.niyaj.poposroom.features.cart.domain.model.OrderWithCart -import com.niyaj.poposroom.features.cart.domain.model.ProductPriceWithQuantity -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderStatus -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.charges.domain.model.ChargesPriceWithApplicable -import com.niyaj.poposroom.features.product.domain.model.Product +import com.niyaj.database.model.AddOnItemEntity +import com.niyaj.database.model.CartEntity +import com.niyaj.database.model.OrderWithCartDto +import com.niyaj.database.model.ProductEntity +import com.niyaj.model.AddOnPriceWithApplicable +import com.niyaj.model.ChargesPriceWithApplicable +import com.niyaj.model.OrderStatus +import com.niyaj.model.OrderType +import com.niyaj.model.ProductPriceWithQuantity import kotlinx.coroutines.flow.Flow @Dao @@ -24,7 +24,7 @@ interface CartDao { @Query(value = """ SELECT * FROM cartorder WHERE orderStatus = :orderStatus ORDER BY createdAt DESC """) - fun getAllCartOrders(orderStatus: OrderStatus = OrderStatus.PROCESSING): Flow> + fun getAllCartOrders(orderStatus: OrderStatus = OrderStatus.PROCESSING): Flow> @Transaction @@ -35,7 +35,7 @@ interface CartDao { fun getAllOrders( orderType: OrderType, orderStatus: OrderStatus = OrderStatus.PROCESSING - ): Flow> + ): Flow> @Transaction @@ -44,7 +44,7 @@ interface CartDao { """) fun getCartProductsByOrderId( orderId: Int - ): Flow + ): Flow @Query(value = """ @@ -87,7 +87,7 @@ interface CartDao { @Query(value = """ SELECT * FROM product WHERE productId = :productId LIMIT 1 """) - suspend fun getProductById(productId: Int): Product + suspend fun getProductById(productId: Int): ProductEntity @Query(value = """ SELECT shortName FROM address WHERE addressId = :addressId @@ -117,5 +117,5 @@ interface CartDao { @Query(value = """ SELECT * FROM addonitem """) - fun getAllAddOnItems(): Flow> + fun getAllAddOnItems(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/data/dao/CartOrderDao.kt b/core/database/src/main/java/com/niyaj/database/dao/CartOrderDao.kt similarity index 80% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/data/dao/CartOrderDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/CartOrderDao.kt index 8c5421b7..4cfdc8cf 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/data/dao/CartOrderDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/CartOrderDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart_order.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -6,16 +6,16 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction import androidx.room.Upsert -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnPriceWithApplicable -import com.niyaj.poposroom.features.cart.domain.model.OrderWithCart -import com.niyaj.poposroom.features.cart.domain.model.ProductPriceWithQuantity -import com.niyaj.poposroom.features.cart_order.domain.model.CartAddOnItems -import com.niyaj.poposroom.features.cart_order.domain.model.CartCharges -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrderEntity -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderStatus -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.charges.domain.model.ChargesPriceWithApplicable +import com.niyaj.database.model.AddOnItemEntity +import com.niyaj.database.model.CartAddOnItemsEntity +import com.niyaj.database.model.CartChargesEntity +import com.niyaj.database.model.CartOrderEntity +import com.niyaj.database.model.ChargesEntity +import com.niyaj.database.model.OrderWithCartDto +import com.niyaj.model.AddOnPriceWithApplicable +import com.niyaj.model.ChargesPriceWithApplicable +import com.niyaj.model.OrderStatus +import com.niyaj.model.ProductPriceWithQuantity import kotlinx.coroutines.flow.Flow import java.util.Date @@ -85,16 +85,16 @@ interface CartOrderDao { @Query(value = """ SELECT * FROM addonitem WHERE itemId = :itemId """) - fun getAddOnItemById(itemId: Int): AddOnItem + fun getAddOnItemById(itemId: Int): AddOnItemEntity @Transaction @Query(value = """ SELECT * FROM charges WHERE chargesId = :chargesId """) - fun getChargesById(chargesId: Int): Charges + fun getChargesById(chargesId: Int): ChargesEntity - @Insert(entity = CartAddOnItems::class, onConflict = OnConflictStrategy.REPLACE) - suspend fun insertCartAddOnItem(items: CartAddOnItems): Long + @Insert(entity = CartAddOnItemsEntity::class, onConflict = OnConflictStrategy.REPLACE) + suspend fun insertCartAddOnItem(items: CartAddOnItemsEntity): Long @Query(value = """ DELETE FROM cart_addon_items WHERE orderId = :orderId AND itemId = :itemId @@ -104,15 +104,15 @@ interface CartOrderDao { @Query(value = """ SELECT * FROM cart_addon_items WHERE orderId = :orderId AND itemId = :itemId """) - suspend fun getCartAddOnItemById(orderId: Int, itemId: Int): CartAddOnItems? + suspend fun getCartAddOnItemById(orderId: Int, itemId: Int): CartAddOnItemsEntity? - @Insert(entity = CartCharges::class, onConflict = OnConflictStrategy.REPLACE) - suspend fun insertCartCharge(items: CartCharges): Long + @Insert(entity = CartChargesEntity::class, onConflict = OnConflictStrategy.REPLACE) + suspend fun insertCartCharge(items: CartChargesEntity): Long @Query(value = """ SELECT * FROM cart_charges WHERE orderId = :orderId AND chargesId = :chargesId """) - suspend fun getCartChargesById(orderId: Int, chargesId: Int): CartCharges? + suspend fun getCartChargesById(orderId: Int, chargesId: Int): CartChargesEntity? @Query(value = """ DELETE FROM cart_charges WHERE orderId = :orderId AND chargesId = :chargesId @@ -127,7 +127,7 @@ interface CartOrderDao { """) suspend fun getCartProductsByOrderId( orderId: Int - ): OrderWithCart + ): OrderWithCartDto @Query(""" @@ -144,7 +144,13 @@ interface CartOrderDao { @Query(value = """ SELECT * FROM cartorder WHERE orderStatus = :orderStatus ORDER BY createdAt DESC """) - fun getAllCartOrders(orderStatus: OrderStatus = OrderStatus.PROCESSING): Flow> + fun getProcessingCartOrders(orderStatus: OrderStatus = OrderStatus.PROCESSING): Flow> + + @Query(value = """ + SELECT * FROM cartorder ORDER BY createdAt DESC + """) + fun getAllCartOrders(): Flow> + @Query(value = """ SELECT * FROM cartorder WHERE orderId = :orderId diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/data/dao/CategoryDao.kt b/core/database/src/main/java/com/niyaj/database/dao/CategoryDao.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/category/data/dao/CategoryDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/CategoryDao.kt index cc26637d..ddb1d393 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/data/dao/CategoryDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/CategoryDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.category.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -6,7 +6,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update import androidx.room.Upsert -import com.niyaj.poposroom.features.category.domain.model.Category +import com.niyaj.database.model.CategoryEntity import kotlinx.coroutines.flow.Flow @Dao @@ -14,30 +14,30 @@ interface CategoryDao { @Query(value = """ SELECT * FROM category ORDER BY createdAt DESC """) - fun getAllCategories(): Flow> + fun getAllCategories(): Flow> @Query(value = """ SELECT * FROM category WHERE categoryId = :categoryId """) - fun getCategoryById(categoryId: Int): Category? + fun getCategoryById(categoryId: Int): CategoryEntity? /** - * Inserts [Category] into the db if they don't exist, and ignores those that do + * Inserts [CategoryEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreCategory(addOnItem: Category): Long + suspend fun insertOrIgnoreCategory(addOnItem: CategoryEntity): Long /** - * Updates [Category] in the db that match the primary key, and no-ops if they don't + * Updates [CategoryEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updateCategory(addOnItem: Category): Int + suspend fun updateCategory(addOnItem: CategoryEntity): Int /** - * Inserts or updates [Category] in the db under the specified primary keys + * Inserts or updates [CategoryEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertCategory(addOnItem: Category): Long + suspend fun upsertCategory(addOnItem: CategoryEntity): Long @Query(value = """ DELETE FROM category WHERE categoryId = :categoryId @@ -62,5 +62,5 @@ interface CategoryDao { ELSE categoryId != :categoryId AND categoryName = :categoryName END LIMIT 1 """) - fun findCategoryByName(categoryName: String, categoryId: Int?): Category? + fun findCategoryByName(categoryName: String, categoryId: Int?): CategoryEntity? } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/data/dao/ChargesDao.kt b/core/database/src/main/java/com/niyaj/database/dao/ChargesDao.kt similarity index 68% rename from app/src/main/java/com/niyaj/poposroom/features/charges/data/dao/ChargesDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/ChargesDao.kt index 83eb7e70..e9da75b1 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/data/dao/ChargesDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/ChargesDao.kt @@ -4,10 +4,9 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query -import androidx.room.Transaction import androidx.room.Update import androidx.room.Upsert -import com.niyaj.poposroom.features.charges.domain.model.Charges +import com.niyaj.database.model.ChargesEntity import kotlinx.coroutines.flow.Flow @Dao @@ -16,30 +15,30 @@ interface ChargesDao { @Query(value = """ SELECT * FROM charges ORDER BY createdAt DESC """) - fun getAllCharges(): Flow> + fun getAllCharges(): Flow> @Query(value = """ SELECT * FROM charges WHERE chargesId = :chargesId """) - fun getChargesById(chargesId: Int): Charges? + fun getChargesById(chargesId: Int): ChargesEntity? /** - * Inserts [Charges] into the db if they don't exist, and ignores those that do + * Inserts [ChargesEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreCharges(charges: Charges): Long + suspend fun insertOrIgnoreCharges(charges: ChargesEntity): Long /** - * Updates [Charges] in the db that match the primary key, and no-ops if they don't + * Updates [ChargesEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updateCharges(charges: Charges): Int + suspend fun updateCharges(charges: ChargesEntity): Int /** - * Inserts or updates [Charges] in the db under the specified primary keys + * Inserts or updates [ChargesEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertCharges(charges: Charges): Long + suspend fun upsertCharges(charges: ChargesEntity): Long @Query(value = """ DELETE FROM charges WHERE chargesId = :chargesId @@ -64,5 +63,5 @@ interface ChargesDao { ELSE chargesId != :chargesId AND chargesName = :chargesName END LIMIT 1 """) - fun findChargesByName(chargesId: Int?, chargesName: String): Charges? + fun findChargesByName(chargesId: Int?, chargesName: String): ChargesEntity? } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/data/dao/CustomerDao.kt b/core/database/src/main/java/com/niyaj/database/dao/CustomerDao.kt similarity index 70% rename from app/src/main/java/com/niyaj/poposroom/features/customer/data/dao/CustomerDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/CustomerDao.kt index 54540d47..839079ae 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/data/dao/CustomerDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/CustomerDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.customer.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -6,7 +6,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update import androidx.room.Upsert -import com.niyaj.poposroom.features.customer.domain.model.Customer +import com.niyaj.database.model.CustomerEntity import kotlinx.coroutines.flow.Flow @Dao @@ -15,12 +15,12 @@ interface CustomerDao { @Query(value = """ SELECT * FROM customer ORDER BY createdAt DESC """) - fun getAllCustomer(): Flow> + fun getAllCustomer(): Flow> @Query(value = """ SELECT * FROM customer WHERE customerId = :customerId """) - fun getCustomerById(customerId: Int): Customer? + fun getCustomerById(customerId: Int): CustomerEntity? /** @@ -33,22 +33,22 @@ interface CustomerDao { suspend fun getCustomerByPhone(customerPhone: String): Int? /** - * Inserts [Customer] into the db if they don't exist, and ignores those that do + * Inserts [CustomerEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreCustomer(customer: Customer): Long + suspend fun insertOrIgnoreCustomer(customer: CustomerEntity): Long /** - * Updates [Customer] in the db that match the primary key, and no-ops if they don't + * Updates [CustomerEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updateCustomer(customer: Customer): Int + suspend fun updateCustomer(customer: CustomerEntity): Int /** - * Inserts or updates [Customer] in the db under the specified primary keys + * Inserts or updates [CustomerEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertCustomer(customer: Customer): Long + suspend fun upsertCustomer(customer: CustomerEntity): Long @Query(value = """ DELETE FROM customer WHERE customerId = :customerId @@ -74,5 +74,5 @@ interface CustomerDao { END LIMIT 1 """ ) - fun findCustomerByPhone(customerPhone: String, customerId: Int?): Customer? + fun findCustomerByPhone(customerPhone: String, customerId: Int?): CustomerEntity? } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/data/dao/EmployeeDao.kt b/core/database/src/main/java/com/niyaj/database/dao/EmployeeDao.kt similarity index 70% rename from app/src/main/java/com/niyaj/poposroom/features/employee/data/dao/EmployeeDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/EmployeeDao.kt index 687f4414..c81db21a 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/data/dao/EmployeeDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/EmployeeDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -6,7 +6,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Update import androidx.room.Upsert -import com.niyaj.poposroom.features.employee.domain.model.Employee +import com.niyaj.database.model.EmployeeEntity import kotlinx.coroutines.flow.Flow @Dao @@ -14,30 +14,30 @@ interface EmployeeDao { @Query(value = """ SELECT * FROM employee ORDER BY createdAt DESC """) - fun getAllEmployee(): Flow> + fun getAllEmployee(): Flow> @Query(value = """ SELECT * FROM employee WHERE employeeId = :employeeId """) - fun getEmployeeById(employeeId: Int): Employee? + fun getEmployeeById(employeeId: Int): EmployeeEntity? /** - * Inserts [Employee] into the db if they don't exist, and ignores those that do + * Inserts [EmployeeEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreEmployee(employee: Employee): Long + suspend fun insertOrIgnoreEmployee(employee: EmployeeEntity): Long /** - * Updates [Employee] in the db that match the primary key, and no-ops if they don't + * Updates [EmployeeEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updateEmployee(employee: Employee): Int + suspend fun updateEmployee(employee: EmployeeEntity): Int /** - * Inserts or updates [Employee] in the db under the specified primary keys + * Inserts or updates [EmployeeEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertEmployee(employee: Employee): Long + suspend fun upsertEmployee(employee: EmployeeEntity): Long @Query(value = """ DELETE FROM employee WHERE employeeId = :employeeId @@ -63,7 +63,7 @@ interface EmployeeDao { END LIMIT 1 """ ) - fun findEmployeeByPhone(employeePhone: String, employeeId: Int?): Employee? + fun findEmployeeByPhone(employeePhone: String, employeeId: Int?): EmployeeEntity? @Query(value = """ SELECT * FROM employee WHERE @@ -73,5 +73,5 @@ interface EmployeeDao { END LIMIT 1 """ ) - fun findEmployeeByName(employeeName: String, employeeId: Int?): Employee? + fun findEmployeeByName(employeeName: String, employeeId: Int?): EmployeeEntity? } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/data/dao/ExpenseDao.kt b/core/database/src/main/java/com/niyaj/database/dao/ExpenseDao.kt similarity index 66% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/data/dao/ExpenseDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/ExpenseDao.kt index a8574033..75f72a47 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/data/dao/ExpenseDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/ExpenseDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.expenses.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -8,7 +8,7 @@ import androidx.room.RawQuery import androidx.room.Update import androidx.room.Upsert import androidx.sqlite.db.SupportSQLiteQuery -import com.niyaj.poposroom.features.expenses.domain.model.Expense +import com.niyaj.database.model.ExpenseEntity import kotlinx.coroutines.flow.Flow @Dao @@ -16,10 +16,10 @@ interface ExpenseDao { @Query(value = """ SELECT * FROM expense WHERE expenseDate = :givenDate ORDER BY expenseDate DESC """) - fun getAllExpense(givenDate: String): Flow> + fun getAllExpense(givenDate: String): Flow> - @RawQuery(observedEntities = [Expense::class]) - fun getAllPagingExpenses(query: SupportSQLiteQuery): List + @RawQuery(observedEntities = [ExpenseEntity::class]) + fun getAllPagingExpenses(query: SupportSQLiteQuery): List @Query(value = """ SELECT DISTINCT expenseName FROM expense ORDER BY expenseDate DESC @@ -29,25 +29,25 @@ interface ExpenseDao { @Query(value = """ SELECT * FROM expense WHERE expenseId = :expenseId """) - fun getExpenseById(expenseId: Int): Expense? + fun getExpenseById(expenseId: Int): ExpenseEntity? /** - * Inserts [Expense] into the db if they don't exist, and ignores those that do + * Inserts [ExpenseEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreExpense(expense: Expense): Long + suspend fun insertOrIgnoreExpense(expense: ExpenseEntity): Long /** - * Updates [Expense] in the db that match the primary key, and no-ops if they don't + * Updates [ExpenseEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updateExpense(expense: Expense): Int + suspend fun updateExpense(expense: ExpenseEntity): Int /** - * Inserts or updates [Expense] in the db under the specified primary keys + * Inserts or updates [ExpenseEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertExpense(expense: Expense): Long + suspend fun upsertExpense(expense: ExpenseEntity): Long @Query(value = """ DELETE FROM expense WHERE expenseId = :expenseId @@ -69,5 +69,5 @@ interface ExpenseDao { SELECT * FROM expense WHERE expenseDate = :expenseDate AND expenseName = :expenseName LIMIT 1 """) - fun findExpenseByNameAndDate(expenseName: String, expenseDate: String): Expense? + fun findExpenseByNameAndDate(expenseName: String, expenseDate: String): ExpenseEntity? } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/main_feed/data/dao/MainFeedDao.kt b/core/database/src/main/java/com/niyaj/database/dao/MainFeedDao.kt similarity index 71% rename from app/src/main/java/com/niyaj/poposroom/features/main_feed/data/dao/MainFeedDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/MainFeedDao.kt index 4f22f629..ca62f1a3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/main_feed/data/dao/MainFeedDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/MainFeedDao.kt @@ -1,10 +1,10 @@ -package com.niyaj.poposroom.features.main_feed.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Query -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.product.domain.model.Product -import com.niyaj.poposroom.features.selected.domain.model.Selected +import com.niyaj.database.model.CategoryEntity +import com.niyaj.database.model.ProductEntity +import com.niyaj.database.model.SelectedEntity import kotlinx.coroutines.flow.Flow @Dao @@ -13,13 +13,13 @@ interface MainFeedDao { @Query(value = """ SELECT * FROM category WHERE isAvailable = :isAvailable ORDER BY categoryId ASC """) - fun getAllCategories(isAvailable: Boolean = true): Flow> + fun getAllCategories(isAvailable: Boolean = true): Flow> @Query(value = """ SELECT * FROM product WHERE productAvailability = :isAvailable ORDER BY categoryId ASC, productPrice ASC """) - fun getAllProducts(isAvailable: Boolean = true): Flow> + fun getAllProducts(isAvailable: Boolean = true): Flow> @Query( @@ -27,7 +27,7 @@ interface MainFeedDao { SELECT * FROM selected LIMIT 1 """ ) - fun getSelectedOrder(): Flow + fun getSelectedOrder(): Flow @Query(value = """ SELECT quantity FROM cart WHERE orderId = :orderId AND productId = :productId diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/data/dao/OrderDao.kt b/core/database/src/main/java/com/niyaj/database/dao/OrderDao.kt similarity index 66% rename from app/src/main/java/com/niyaj/poposroom/features/order/data/dao/OrderDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/OrderDao.kt index 59248f2f..201c7776 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/data/dao/OrderDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/OrderDao.kt @@ -1,14 +1,14 @@ -package com.niyaj.poposroom.features.order.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Query import androidx.room.Transaction -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.cart.domain.model.OrderWithCart -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderStatus -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.customer.domain.model.Customer -import com.niyaj.poposroom.features.product.domain.model.Product +import com.niyaj.database.model.AddressEntity +import com.niyaj.database.model.ChargesEntity +import com.niyaj.database.model.CustomerEntity +import com.niyaj.database.model.OrderWithCartDto +import com.niyaj.database.model.ProductEntity +import com.niyaj.model.OrderStatus import kotlinx.coroutines.flow.Flow @Dao @@ -23,13 +23,13 @@ interface OrderDao { startDate: Long, endDate: Long, orderStatus: OrderStatus = OrderStatus.PLACED - ): Flow> + ): Flow> @Transaction @Query(value = """ SELECT * FROM cartorder WHERE orderId = :orderId """) - fun getOrderDetails(orderId: Int): Flow + fun getOrderDetails(orderId: Int): Flow // ---------------------------------------------------------------- @@ -52,21 +52,21 @@ interface OrderDao { @Query(value = """ SELECT * FROM product WHERE productId = :productId """) - suspend fun getProductById(productId: Int): Product + suspend fun getProductById(productId: Int): ProductEntity @Query(value = """ SELECT * FROM address WHERE addressId = :addressId """) - suspend fun getAddressById(addressId: Int): Address + suspend fun getAddressById(addressId: Int): AddressEntity @Query(value = """ SELECT * FROM customer WHERE customerId = :customerId """) - suspend fun getCustomerById(customerId: Int): Customer + suspend fun getCustomerById(customerId: Int): CustomerEntity @Query(value = """ SELECT * FROM charges """) - fun getAllCharges(): Flow> + fun getAllCharges(): Flow> } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/data/dao/PaymentDao.kt b/core/database/src/main/java/com/niyaj/database/dao/PaymentDao.kt similarity index 59% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/data/dao/PaymentDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/PaymentDao.kt index 48383885..8c6f6a14 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/data/dao/PaymentDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/PaymentDao.kt @@ -7,10 +7,10 @@ import androidx.room.Query import androidx.room.Transaction import androidx.room.Update import androidx.room.Upsert -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee_payment.domain.model.EmployeeWithPayment -import com.niyaj.poposroom.features.employee_payment.domain.model.EmployeeWithPaymentCrossRef -import com.niyaj.poposroom.features.employee_payment.domain.model.Payment +import com.niyaj.database.model.EmployeeEntity +import com.niyaj.database.model.EmployeeWithPaymentCrossRef +import com.niyaj.database.model.EmployeeWithPaymentsDto +import com.niyaj.database.model.PaymentEntity import kotlinx.coroutines.flow.Flow @Dao @@ -20,47 +20,47 @@ interface PaymentDao { @Query(value = """ SELECT * FROM employee """) - fun getAllEmployeePayment(): Flow> + fun getAllEmployeePayment(): Flow> @Query(value = """ SELECT * FROM employee """) - fun getAllEmployee(): Flow> + fun getAllEmployee(): Flow> @Query(value = """ SELECT * FROM employee WHERE employeeId = :employeeId """ ) - suspend fun getEmployeeById(employeeId: Int): Employee? + suspend fun getEmployeeById(employeeId: Int): EmployeeEntity? @Query(value = """ SELECT * FROM payment ORDER BY createdAt DESC """) - fun getAllPayment(): Flow> + fun getAllPayment(): Flow> @Query(value = """ SELECT * FROM payment WHERE paymentId = :paymentId """) - suspend fun getPaymentById(paymentId: Int): Payment? + suspend fun getPaymentById(paymentId: Int): PaymentEntity? /** - * Inserts [Payment] into the db if they don't exist, and ignores those that do + * Inserts [PaymentEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnorePayment(payment: Payment): Long + suspend fun insertOrIgnorePayment(payment: PaymentEntity): Long /** - * Updates [Payment] in the db that match the primary key, and no-ops if they don't + * Updates [PaymentEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updatePayment(payment: Payment): Int + suspend fun updatePayment(payment: PaymentEntity): Int /** - * Inserts or updates [Payment] in the db under the specified primary keys + * Inserts or updates [PaymentEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertPayment(payment: Payment): Long + suspend fun upsertPayment(payment: PaymentEntity): Long @Insert(entity = EmployeeWithPaymentCrossRef::class, onConflict = OnConflictStrategy.REPLACE) suspend fun upsertEmployeeWithPaymentCrossReference(employeeWithPayment: EmployeeWithPaymentCrossRef) diff --git a/core/database/src/main/java/com/niyaj/database/dao/PrintDao.kt b/core/database/src/main/java/com/niyaj/database/dao/PrintDao.kt new file mode 100644 index 00000000..84e20949 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/dao/PrintDao.kt @@ -0,0 +1,8 @@ +package com.niyaj.database.dao + +import androidx.room.Dao + +@Dao +interface PrintDao { + +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/dao/PrinterDao.kt b/core/database/src/main/java/com/niyaj/database/dao/PrinterDao.kt new file mode 100644 index 00000000..d4e5168c --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/dao/PrinterDao.kt @@ -0,0 +1,26 @@ +package com.niyaj.database.dao + +import androidx.room.Dao +import androidx.room.Query +import androidx.room.Upsert +import com.niyaj.database.model.PrinterEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface PrinterDao { + + @Query( + value = """ + SELECT * FROM printerInfo WHERE printerId = :printerId + """ + ) + suspend fun printerInfo(printerId: String): PrinterEntity? + + + /** + * Inserts or update [PrinterEntity] in the db under the specified primary keys + */ + @Upsert + suspend fun insertOrUpdatePrinterInfo(printerEntity: PrinterEntity): Long + +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/data/dao/ProductDao.kt b/core/database/src/main/java/com/niyaj/database/dao/ProductDao.kt similarity index 63% rename from app/src/main/java/com/niyaj/poposroom/features/product/data/dao/ProductDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/ProductDao.kt index 64b9d700..e693670e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/data/dao/ProductDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/ProductDao.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Insert @@ -7,10 +7,10 @@ import androidx.room.Query import androidx.room.Transaction import androidx.room.Update import androidx.room.Upsert -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.product.domain.model.CategoryWithProduct -import com.niyaj.poposroom.features.product.domain.model.CategoryWithProductCrossRef -import com.niyaj.poposroom.features.product.domain.model.Product +import com.niyaj.database.model.CategoryEntity +import com.niyaj.database.model.CategoryWithProductCrossRef +import com.niyaj.database.model.CategoryWithProductsDto +import com.niyaj.database.model.ProductEntity import kotlinx.coroutines.flow.Flow @Dao @@ -20,46 +20,46 @@ interface ProductDao { @Query(value = """ SELECT * FROM category """) - fun getAllCategoryProduct(): Flow> + fun getAllCategoryProduct(): Flow> @Query(value = """ SELECT * FROM category """) - fun getAllCategory(): Flow> + fun getAllCategory(): Flow> @Query(value = """ SELECT * FROM category WHERE categoryId = :categoryId """ ) - suspend fun getCategoryById(categoryId: Int): Category? + suspend fun getCategoryById(categoryId: Int): CategoryEntity? @Query(value = """ SELECT * FROM product ORDER BY categoryId ASC, productPrice ASC """) - fun getAllProduct(): Flow> + fun getAllProduct(): Flow> @Query(value = """ SELECT * FROM product WHERE productId = :productId """) - suspend fun getProductById(productId: Int): Product? + suspend fun getProductById(productId: Int): ProductEntity? /** - * Inserts [Product] into the db if they don't exist, and ignores those that do + * Inserts [ProductEntity] into the db if they don't exist, and ignores those that do */ @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreProduct(product: Product): Long + suspend fun insertOrIgnoreProduct(product: ProductEntity): Long /** - * Updates [Product] in the db that match the primary key, and no-ops if they don't + * Updates [ProductEntity] in the db that match the primary key, and no-ops if they don't */ @Update - suspend fun updateProduct(product: Product): Int + suspend fun updateProduct(product: ProductEntity): Int /** - * Inserts or updates [Product] in the db under the specified primary keys + * Inserts or updates [ProductEntity] in the db under the specified primary keys */ @Upsert - suspend fun upsertProduct(product: Product): Long + suspend fun upsertProduct(product: ProductEntity): Long @Insert(entity = CategoryWithProductCrossRef::class, onConflict = OnConflictStrategy.REPLACE) suspend fun upsertCategoryWithProductCrossReference(categoryWithProduct: CategoryWithProductCrossRef) @@ -88,6 +88,6 @@ interface ProductDao { END LIMIT 1 """ ) - fun findProductByName(productName: String, productId: Int?): Product? + fun findProductByName(productName: String, productId: Int?): ProductEntity? } \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/dao/ProfileDao.kt b/core/database/src/main/java/com/niyaj/database/dao/ProfileDao.kt new file mode 100644 index 00000000..5893c714 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/dao/ProfileDao.kt @@ -0,0 +1,32 @@ +package com.niyaj.database.dao + +import androidx.room.Dao +import androidx.room.Query +import androidx.room.Upsert +import com.niyaj.common.utils.Constants.RESTAURANT_ID +import com.niyaj.database.model.ProfileEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface ProfileDao { + + @Query(value = """ + SELECT * FROM profile WHERE restaurantId = :restaurantId + """) + fun getProfileInfo(restaurantId: Int = RESTAURANT_ID): Flow + + @Query(value = """ + SELECT * FROM profile WHERE restaurantId = :restaurantId + """) + fun getProfileById(restaurantId: Int = RESTAURANT_ID): ProfileEntity? + +// @Query( +// value = """ +// +// """ +// ) +// suspend fun updateProfileLogo(resLogo: String, restaurantId: Int = RESTAURANT_ID): Long + + @Upsert + suspend fun insertOrUpdateProfile(profile: ProfileEntity): Long +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/selected/data/dao/SelectedDao.kt b/core/database/src/main/java/com/niyaj/database/dao/SelectedDao.kt similarity index 57% rename from app/src/main/java/com/niyaj/poposroom/features/selected/data/dao/SelectedDao.kt rename to core/database/src/main/java/com/niyaj/database/dao/SelectedDao.kt index 92b3e779..798a7323 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/selected/data/dao/SelectedDao.kt +++ b/core/database/src/main/java/com/niyaj/database/dao/SelectedDao.kt @@ -1,12 +1,12 @@ -package com.niyaj.poposroom.features.selected.data.dao +package com.niyaj.database.dao import androidx.room.Dao import androidx.room.Query import androidx.room.Upsert -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrderEntity -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderStatus -import com.niyaj.poposroom.features.selected.domain.model.Selected -import com.niyaj.poposroom.features.selected.domain.utils.SelectedTestTag.SELECTED_ID +import com.niyaj.database.model.CartOrderEntity +import com.niyaj.database.model.SelectedEntity +import com.niyaj.model.OrderStatus +import com.niyaj.model.Selected import kotlinx.coroutines.flow.Flow @Dao @@ -17,7 +17,7 @@ interface SelectedDao { SELECT * FROM selected LIMIT 1 """ ) - fun getSelectedCartOrder(): Flow + fun getSelectedCartOrder(): Flow @Query( value = """ @@ -27,14 +27,14 @@ interface SelectedDao { fun getSelectedCartOrderId(): Int? @Upsert - suspend fun insertOrUpdateSelectedOrder(selected: Selected): Long + suspend fun insertOrUpdateSelectedOrder(selected: SelectedEntity): Long @Query( value = """ DELETE FROM selected WHERE selectedId = :id """ ) - suspend fun deleteSelectedOrder(id: String = SELECTED_ID) + suspend fun deleteSelectedOrder(id: String) @Query( value = """ diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/model/Absent.kt b/core/database/src/main/java/com/niyaj/database/model/AbsentEntity.kt similarity index 58% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/model/Absent.kt rename to core/database/src/main/java/com/niyaj/database/model/AbsentEntity.kt index 17028e5a..3cb59002 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/model/Absent.kt +++ b/core/database/src/main/java/com/niyaj/database/model/AbsentEntity.kt @@ -1,23 +1,22 @@ -package com.niyaj.poposroom.features.employee_absent.domain.model +package com.niyaj.database.model import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.PrimaryKey -import com.niyaj.poposroom.features.common.utils.toJoinedDate -import com.niyaj.poposroom.features.employee.domain.model.Employee +import com.niyaj.model.Absent import java.util.Date @Entity( tableName = "absent", foreignKeys = [ForeignKey( - entity = Employee::class, + entity = EmployeeEntity::class, parentColumns = arrayOf("employeeId"), childColumns = arrayOf("employeeId"), onDelete = ForeignKey.CASCADE )] ) -data class Absent( +data class AbsentEntity( @PrimaryKey(autoGenerate = true) val absentId: Int = 0, @@ -26,7 +25,7 @@ data class Absent( val absentReason: String = "", - val absentDate: String = "", + val absentDate: String, @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date, @@ -35,11 +34,13 @@ data class Absent( val updatedAt: Date? = null, ) - -/** - * - */ -fun Absent.filterAbsent(searchText: String): Boolean { - return this.absentDate.toJoinedDate.contains(searchText, true) || - this.absentReason.contains(searchText, true) -} +fun AbsentEntity.asExternalModel(): Absent { + return Absent( + absentId = this.absentId, + employeeId = this.employeeId, + absentReason = this.absentReason, + absentDate = this.absentDate, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/AddOnItemEntity.kt b/core/database/src/main/java/com/niyaj/database/model/AddOnItemEntity.kt new file mode 100644 index 00000000..c3615899 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/AddOnItemEntity.kt @@ -0,0 +1,38 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.model.AddOnItem +import java.util.Date + +@Entity(tableName = "addonitem") +data class AddOnItemEntity( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(index = true) + val itemId: Int, + + val itemName: String, + + val itemPrice: Int, + + val isApplicable: Boolean, + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date, + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null, +) + + +fun AddOnItemEntity.asExternalModel(): AddOnItem { + return AddOnItem( + itemId = this.itemId, + itemName = this.itemName, + itemPrice = this.itemPrice, + isApplicable = this.isApplicable, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/AddressEntity.kt b/core/database/src/main/java/com/niyaj/database/model/AddressEntity.kt new file mode 100644 index 00000000..0296b6ca --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/AddressEntity.kt @@ -0,0 +1,34 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.model.Address +import java.util.Date + +@Entity(tableName = "address") +data class AddressEntity( + @PrimaryKey(autoGenerate = true) + val addressId: Int = 0, + + val addressName: String = "", + + val shortName: String = "", + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date = Date(), + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null, +) + + +fun AddressEntity.asExternalModel(): Address { + return Address( + addressId = this.addressId, + addressName = this.addressName, + shortName = this.shortName, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/model/CartAddOnItems.kt b/core/database/src/main/java/com/niyaj/database/model/CartAddOnItemsEntity.kt similarity index 75% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/model/CartAddOnItems.kt rename to core/database/src/main/java/com/niyaj/database/model/CartAddOnItemsEntity.kt index d57fe68b..6d91935d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/model/CartAddOnItems.kt +++ b/core/database/src/main/java/com/niyaj/database/model/CartAddOnItemsEntity.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart_order.domain.model +package com.niyaj.database.model import androidx.room.ColumnInfo import androidx.room.Embedded @@ -6,7 +6,6 @@ import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Junction import androidx.room.Relation -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem import java.util.Date @Entity( @@ -21,7 +20,7 @@ import java.util.Date onUpdate = ForeignKey.NO_ACTION ), ForeignKey( - entity = AddOnItem::class, + entity = AddOnItemEntity::class, parentColumns = arrayOf("itemId"), childColumns = arrayOf("itemId"), onDelete = ForeignKey.CASCADE, @@ -29,7 +28,7 @@ import java.util.Date ) ] ) -data class CartAddOnItems( +data class CartAddOnItemsEntity( @ColumnInfo(index = true) val orderId: Int, @@ -41,30 +40,29 @@ data class CartAddOnItems( ) - -data class CartOrderWithAddOnItemsId( +data class CartOrderWithAddOnItemsIdDto( @Embedded val cartOrderEntity: CartOrderEntity, @Relation( parentColumn = "orderId", - entity = AddOnItem::class, + entity = AddOnItemEntity::class, entityColumn = "itemId", - associateBy = Junction(CartAddOnItems::class), + associateBy = Junction(CartAddOnItemsEntity::class), projection = ["itemId"] ) val items: List = emptyList() ) -data class CartOrderWithAddOnItemsPrice( +data class CartOrderWithAddOnItemsPriceDto( @Embedded val cartOrderEntity: CartOrderEntity, @Relation( parentColumn = "orderId", - entity = AddOnItem::class, + entity = AddOnItemEntity::class, entityColumn = "itemId", - associateBy = Junction(CartAddOnItems::class), + associateBy = Junction(CartAddOnItemsEntity::class), projection = ["itemPrice"] ) val items: List = emptyList() diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/model/CartCharges.kt b/core/database/src/main/java/com/niyaj/database/model/CartChargesEntity.kt similarity index 76% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/model/CartCharges.kt rename to core/database/src/main/java/com/niyaj/database/model/CartChargesEntity.kt index cbc9c126..7b2b3c81 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/domain/model/CartCharges.kt +++ b/core/database/src/main/java/com/niyaj/database/model/CartChargesEntity.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart_order.domain.model +package com.niyaj.database.model import androidx.room.ColumnInfo import androidx.room.Embedded @@ -6,7 +6,6 @@ import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Junction import androidx.room.Relation -import com.niyaj.poposroom.features.charges.domain.model.Charges import java.util.Date @Entity( @@ -21,7 +20,7 @@ import java.util.Date onUpdate = ForeignKey.NO_ACTION ), ForeignKey( - entity = Charges::class, + entity = ChargesEntity::class, parentColumns = arrayOf("chargesId"), childColumns = arrayOf("chargesId"), onDelete = ForeignKey.CASCADE, @@ -29,7 +28,7 @@ import java.util.Date ) ] ) -data class CartCharges( +data class CartChargesEntity( @ColumnInfo(index = true) val orderId: Int, @@ -41,29 +40,29 @@ data class CartCharges( ) -data class CartOrderWithChargesId( +data class CartOrderWithChargesIdDto( @Embedded val cartOrderEntity: CartOrderEntity, @Relation( parentColumn = "orderId", - entity = Charges::class, + entity = ChargesEntity::class, entityColumn = "chargesId", - associateBy = Junction(CartCharges::class), + associateBy = Junction(CartChargesEntity::class), projection = ["chargesId"] ) val items: List = emptyList() ) -data class CartOrderWithChargesPrice( +data class CartOrderWithChargesPriceDto( @Embedded val cartOrderEntity: CartOrderEntity, @Relation( parentColumn = "orderId", - entity = Charges::class, + entity = ChargesEntity::class, entityColumn = "chargesId", - associateBy = Junction(CartCharges::class), + associateBy = Junction(CartChargesEntity::class), projection = ["chargesId"] ) val items: List = emptyList() diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/CartEntity.kt b/core/database/src/main/java/com/niyaj/database/model/CartEntity.kt similarity index 67% rename from app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/CartEntity.kt rename to core/database/src/main/java/com/niyaj/database/model/CartEntity.kt index db9bbf98..94babbe8 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/CartEntity.kt +++ b/core/database/src/main/java/com/niyaj/database/model/CartEntity.kt @@ -1,11 +1,10 @@ -package com.niyaj.poposroom.features.cart.domain.model +package com.niyaj.database.model import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.PrimaryKey -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrderEntity -import com.niyaj.poposroom.features.product.domain.model.Product +import com.niyaj.model.Cart import java.util.Date @Entity( @@ -18,7 +17,7 @@ import java.util.Date onDelete = ForeignKey.CASCADE ), ForeignKey( - entity = Product::class, + entity = ProductEntity::class, parentColumns = arrayOf("productId"), childColumns = arrayOf("productId"), onDelete = ForeignKey.CASCADE @@ -30,12 +29,12 @@ data class CartEntity( val cartId: Int = 0, @ColumnInfo(index = true) - val orderId: Int = 0, + val orderId: Int, @ColumnInfo(index = true) - val productId: Int = 0, + val productId: Int, - val quantity: Int = 0, + val quantity: Int, @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date = Date(), @@ -44,10 +43,13 @@ data class CartEntity( val updatedAt: Date? = null, ) - -data class ProductPriceWithQuantity( - - val productPrice: Int, - - val quantity: Int -) \ No newline at end of file +fun CartEntity.asExternalModel(): Cart { + return Cart( + cartId = this.cartId, + orderId = this.orderId, + productId = this.productId, + quantity = this.quantity, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/CartOrderEntity.kt b/core/database/src/main/java/com/niyaj/database/model/CartOrderEntity.kt new file mode 100644 index 00000000..c2822f49 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/CartOrderEntity.kt @@ -0,0 +1,33 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.model.OrderStatus +import com.niyaj.model.OrderType +import java.util.Date + + +@Entity(tableName = "cartorder") +data class CartOrderEntity( + @PrimaryKey(autoGenerate = true) + val orderId: Int, + + val orderType: OrderType, + + val orderStatus: OrderStatus, + + val doesChargesIncluded: Boolean, + + @ColumnInfo(index = true) + val addressId: Int, + + @ColumnInfo(index = true) + val customerId: Int, + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date = Date(), + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null, +) \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/CategoryEntity.kt b/core/database/src/main/java/com/niyaj/database/model/CategoryEntity.kt new file mode 100644 index 00000000..d2556df9 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/CategoryEntity.kt @@ -0,0 +1,33 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.model.Category +import java.util.Date + +@Entity(tableName = "category") +data class CategoryEntity( + @PrimaryKey(autoGenerate = true) + val categoryId: Int = 0, + + val categoryName: String, + + val isAvailable: Boolean, + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date, + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null, +) + +fun CategoryEntity.asExternalModel(): Category { + return Category( + categoryId = this.categoryId, + categoryName = this.categoryName, + isAvailable = this.isAvailable, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/domain/model/CategoryWithProductCrossRef.kt b/core/database/src/main/java/com/niyaj/database/model/CategoryWithProductCrossRef.kt similarity index 63% rename from app/src/main/java/com/niyaj/poposroom/features/product/domain/model/CategoryWithProductCrossRef.kt rename to core/database/src/main/java/com/niyaj/database/model/CategoryWithProductCrossRef.kt index 13080143..e84fb897 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/domain/model/CategoryWithProductCrossRef.kt +++ b/core/database/src/main/java/com/niyaj/database/model/CategoryWithProductCrossRef.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.domain.model +package com.niyaj.database.model import androidx.room.ColumnInfo import androidx.room.Embedded @@ -6,21 +6,21 @@ import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Junction import androidx.room.Relation -import com.niyaj.poposroom.features.category.domain.model.Category +import com.niyaj.model.CategoryWithProducts @Entity( primaryKeys = ["productId", "categoryId"], foreignKeys = [ ForeignKey( - entity = Product::class, + entity = ProductEntity::class, parentColumns = arrayOf("productId"), childColumns = arrayOf("productId"), onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.NO_ACTION ), ForeignKey( - entity = Category::class, + entity = CategoryEntity::class, parentColumns = arrayOf("categoryId"), childColumns = arrayOf("categoryId"), onDelete = ForeignKey.CASCADE, @@ -33,19 +33,26 @@ data class CategoryWithProductCrossRef( val categoryId: Int, @ColumnInfo(index = true) - val productId: Int + val productId: Int, ) -data class CategoryWithProduct( +data class CategoryWithProductsDto( @Embedded - val category: Category, + val category: CategoryEntity, @Relation( parentColumn = "categoryId", - entity = Product::class, + entity = ProductEntity::class, entityColumn = "productId", associateBy = Junction(CategoryWithProductCrossRef::class) ) - val products: List = emptyList() -) \ No newline at end of file + val products: List = emptyList(), +) + +fun CategoryWithProductsDto.asExternalModel(): CategoryWithProducts { + return CategoryWithProducts( + category = this.category.asExternalModel(), + products = this.products.map { it.asExternalModel() } + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/ChargesEntity.kt b/core/database/src/main/java/com/niyaj/database/model/ChargesEntity.kt new file mode 100644 index 00000000..bc4abee5 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/ChargesEntity.kt @@ -0,0 +1,37 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.model.Charges +import java.util.Date + +@Entity(tableName = "charges") +data class ChargesEntity( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(index = true) + val chargesId: Int, + + val chargesName: String, + + val chargesPrice: Int, + + val isApplicable: Boolean, + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date, + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null, +) + +fun ChargesEntity.asExternalModel(): Charges { + return Charges( + chargesId = this.chargesId, + chargesName = this.chargesName, + chargesPrice = this.chargesPrice, + isApplicable = this.isApplicable, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/CustomerEntity.kt b/core/database/src/main/java/com/niyaj/database/model/CustomerEntity.kt new file mode 100644 index 00000000..27898c2e --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/CustomerEntity.kt @@ -0,0 +1,36 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.model.Customer +import java.util.Date + +@Entity(tableName = "customer") +data class CustomerEntity( + @PrimaryKey(autoGenerate = true) + val customerId: Int = 0, + + val customerPhone: String, + + val customerName: String? = null, + + val customerEmail: String? = null, + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date, + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null, +) + +fun CustomerEntity.asExternalModel(): Customer { + return Customer( + customerId = this.customerId, + customerName = this.customerName, + customerPhone = this.customerPhone, + customerEmail = this.customerEmail, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/EmployeeEntity.kt b/core/database/src/main/java/com/niyaj/database/model/EmployeeEntity.kt new file mode 100644 index 00000000..4a5d8c36 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/EmployeeEntity.kt @@ -0,0 +1,54 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.model.Employee +import com.niyaj.model.EmployeeSalaryType +import com.niyaj.model.EmployeeType +import java.util.Date + +@Entity(tableName = "employee") +data class EmployeeEntity( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(index = true) + val employeeId: Int = 0, + + val employeeName: String = "", + + val employeePhone: String = "", + + val employeeSalary: String = "", + + val employeePosition: String = "", + + val employeeJoinedDate: String = "", + + val employeeEmail: String? = null, + + val employeeSalaryType: EmployeeSalaryType = EmployeeSalaryType.Daily, + + val employeeType: EmployeeType = EmployeeType.FullTime, + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date = Date(), + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null, +) + +fun EmployeeEntity.asExternalModel(): Employee { + return Employee( + employeeId = this.employeeId, + employeeName = this.employeeName, + employeePhone = this.employeePhone, + employeeSalary = this.employeeSalary, + employeePosition = this.employeePosition, + employeeJoinedDate = this.employeeJoinedDate, + employeeEmail = this.employeeEmail, + employeeSalaryType = this.employeeSalaryType, + employeeType = this.employeeType, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/model/EmployeeWithAbsentCrossRef.kt b/core/database/src/main/java/com/niyaj/database/model/EmployeeWithAbsentCrossRef.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/model/EmployeeWithAbsentCrossRef.kt rename to core/database/src/main/java/com/niyaj/database/model/EmployeeWithAbsentCrossRef.kt index 71967a7c..179b4fd5 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/domain/model/EmployeeWithAbsentCrossRef.kt +++ b/core/database/src/main/java/com/niyaj/database/model/EmployeeWithAbsentCrossRef.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_absent.domain.model +package com.niyaj.database.model import androidx.room.ColumnInfo import androidx.room.Embedded @@ -6,20 +6,20 @@ import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Junction import androidx.room.Relation -import com.niyaj.poposroom.features.employee.domain.model.Employee +import com.niyaj.model.EmployeeWithAbsents @Entity( primaryKeys = ["employeeId", "absentId"], foreignKeys = [ ForeignKey( - entity = Employee::class, + entity = EmployeeEntity::class, parentColumns = arrayOf("employeeId"), childColumns = arrayOf("employeeId"), onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.NO_ACTION ), ForeignKey( - entity = Absent::class, + entity = AbsentEntity::class, parentColumns = arrayOf("absentId"), childColumns = arrayOf("absentId"), onDelete = ForeignKey.CASCADE, @@ -36,16 +36,22 @@ data class EmployeeWithAbsentCrossRef( ) - -data class EmployeeWithAbsent( +data class EmployeeWithAbsentsDto( @Embedded - val employee: Employee, + val employee: EmployeeEntity, @Relation( parentColumn = "employeeId", - entity = Absent::class, + entity = AbsentEntity::class, entityColumn = "absentId", associateBy = Junction(EmployeeWithAbsentCrossRef::class) ) - val absents: List = emptyList() -) \ No newline at end of file + val absents: List = emptyList() +) + +fun EmployeeWithAbsentsDto.asExternalModel(): EmployeeWithAbsents { + return EmployeeWithAbsents( + employee = this.employee.asExternalModel(), + absents = this.absents.map { it.asExternalModel() } + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/model/EmployeeWithPayment.kt b/core/database/src/main/java/com/niyaj/database/model/EmployeeWithPaymentCrossRef.kt similarity index 57% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/model/EmployeeWithPayment.kt rename to core/database/src/main/java/com/niyaj/database/model/EmployeeWithPaymentCrossRef.kt index 7fff836f..a5be08be 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/model/EmployeeWithPayment.kt +++ b/core/database/src/main/java/com/niyaj/database/model/EmployeeWithPaymentCrossRef.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_payment.domain.model +package com.niyaj.database.model import androidx.room.ColumnInfo import androidx.room.Embedded @@ -6,21 +6,20 @@ import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.Junction import androidx.room.Relation -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee.domain.model.filterEmployee +import com.niyaj.model.EmployeeWithPayments @Entity( primaryKeys = ["employeeId", "paymentId"], foreignKeys = [ ForeignKey( - entity = Employee::class, + entity = EmployeeEntity::class, parentColumns = arrayOf("employeeId"), childColumns = arrayOf("employeeId"), onDelete = ForeignKey.CASCADE, onUpdate = ForeignKey.NO_ACTION ), ForeignKey( - entity = Payment::class, + entity = PaymentEntity::class, parentColumns = arrayOf("paymentId"), childColumns = arrayOf("paymentId"), onDelete = ForeignKey.CASCADE, @@ -36,25 +35,22 @@ data class EmployeeWithPaymentCrossRef( val paymentId: Int ) -data class EmployeeWithPayment( +data class EmployeeWithPaymentsDto( @Embedded - val employee: Employee, + val employee: EmployeeEntity, @Relation( parentColumn = "employeeId", - entity = Payment::class, + entity = PaymentEntity::class, entityColumn = "paymentId", associateBy = Junction(EmployeeWithPaymentCrossRef::class) ) - val payments: List = emptyList() + val payments: List = emptyList() ) - -fun List.searchEmployeeWithPayments(searchText: String): List { - return if (searchText.isNotEmpty()) { - this.filter { withSalary -> - withSalary.employee.filterEmployee(searchText) || - withSalary.payments.any { it.filterPayment(searchText) } - } - } else this +fun EmployeeWithPaymentsDto.asExternalModel(): EmployeeWithPayments { + return EmployeeWithPayments( + employee = this.employee.asExternalModel(), + payments = this.payments.map { it.asExternalModel() } + ) } \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/ExpenseEntity.kt b/core/database/src/main/java/com/niyaj/database/model/ExpenseEntity.kt new file mode 100644 index 00000000..80d7e247 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/ExpenseEntity.kt @@ -0,0 +1,39 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.model.Expense +import java.util.Date + +@Entity(tableName = "expense") +data class ExpenseEntity( + @PrimaryKey(autoGenerate = true) + val expenseId: Int = 0, + + val expenseName: String = "", + + val expenseAmount: String = "", + + val expenseDate: String = "", + + val expenseNote: String = "", + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date = Date(), + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null, +) + +fun ExpenseEntity.asExternalModel(): Expense { + return Expense( + expenseId = this.expenseId, + expenseName = this.expenseName, + expenseAmount = this.expenseAmount, + expenseDate = this.expenseDate, + expenseNote = this.expenseNote, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/OrderWithCart.kt b/core/database/src/main/java/com/niyaj/database/model/OrderWithCartDto.kt similarity index 57% rename from app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/OrderWithCart.kt rename to core/database/src/main/java/com/niyaj/database/model/OrderWithCartDto.kt index b1b6fa5d..3621558b 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/OrderWithCart.kt +++ b/core/database/src/main/java/com/niyaj/database/model/OrderWithCartDto.kt @@ -1,10 +1,14 @@ -package com.niyaj.poposroom.features.cart.domain.model +package com.niyaj.database.model import androidx.room.Embedded import androidx.room.Relation -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrderEntity +import com.niyaj.model.Address +import com.niyaj.model.CartOrder +import com.niyaj.model.Customer +import com.niyaj.model.OrderWithCartItems +import com.niyaj.model.ProductAndQuantity -data class OrderWithCart( +data class OrderWithCartDto( @Embedded val cartOrder: CartOrderEntity, @@ -15,7 +19,4 @@ data class OrderWithCart( projection = ["productId", "quantity"] ) val cartItems: List, -) - - -data class ProductAndQuantity(val productId: Int, val quantity: Int) \ No newline at end of file +) \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/PaymentEntity.kt b/core/database/src/main/java/com/niyaj/database/model/PaymentEntity.kt new file mode 100644 index 00000000..d171320a --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/PaymentEntity.kt @@ -0,0 +1,58 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey +import com.niyaj.model.Payment +import com.niyaj.model.PaymentMode +import com.niyaj.model.PaymentType +import java.util.Date + +@Entity( + tableName = "payment", + foreignKeys = [ForeignKey( + entity = EmployeeEntity::class, + parentColumns = arrayOf("employeeId"), + childColumns = arrayOf("employeeId"), + onDelete = ForeignKey.CASCADE + )] +) +data class PaymentEntity( + @PrimaryKey(autoGenerate = true) + @ColumnInfo(index = true) + val paymentId: Int = 0, + + @ColumnInfo(index = true) + val employeeId: Int, + + val paymentAmount: String = "", + + val paymentDate: String = "", + + val paymentType: PaymentType = PaymentType.Advanced, + + val paymentMode: PaymentMode = PaymentMode.Cash, + + val paymentNote: String = "", + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date, + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null, +) + +fun PaymentEntity.asExternalModel(): Payment { + return Payment( + paymentId = this.paymentId, + employeeId = this.employeeId, + paymentAmount = this.paymentAmount, + paymentDate = this.paymentDate, + paymentType = this.paymentType, + paymentMode = this.paymentMode, + paymentNote = this.paymentNote, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/PrinterEntity.kt b/core/database/src/main/java/com/niyaj/database/model/PrinterEntity.kt new file mode 100644 index 00000000..56c770c4 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/PrinterEntity.kt @@ -0,0 +1,65 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.common.utils.Constants.PRINTER_DPI +import com.niyaj.common.utils.Constants.PRINTER_ID +import com.niyaj.common.utils.Constants.PRINTER_NBR_LINE +import com.niyaj.common.utils.Constants.PRINTER_WIDTH_MM +import com.niyaj.common.utils.Constants.PRINT_ADDRESS_WISE_REPORT_LIMIT +import com.niyaj.common.utils.Constants.PRINT_CUSTOMER_WISE_REPORT_LIMIT +import com.niyaj.common.utils.Constants.PRINT_PRODUCT_WISE_REPORT_LIMIT +import com.niyaj.common.utils.Constants.PRODUCT_NAME_LENGTH +import com.niyaj.model.Printer + +@Entity( + tableName = "printerInfo" +) +data class PrinterEntity( + @PrimaryKey + @ColumnInfo(index = true) + val printerId: String = PRINTER_ID, + + val printerDpi: Int = PRINTER_DPI, + + val printerWidth: Float = PRINTER_WIDTH_MM, + + val printerNbrLines: Int = PRINTER_NBR_LINE, + + val productNameLength: Int = PRODUCT_NAME_LENGTH, + + val productWiseReportLimit: Int = PRINT_PRODUCT_WISE_REPORT_LIMIT, + + val addressWiseReportLimit: Int = PRINT_ADDRESS_WISE_REPORT_LIMIT, + + val customerWiseReportLimit: Int = PRINT_CUSTOMER_WISE_REPORT_LIMIT, + + val printQRCode: Boolean = true, + + val printResLogo: Boolean = true, + + val printWelcomeText: Boolean = true, + + val createdAt: String = System.currentTimeMillis().toString(), + + val updatedAt: String? = null, +) + +fun PrinterEntity.toExternalModel(): Printer { + return Printer( + printerId = this.printerId, + printerDpi = this.printerDpi, + printerWidth = this.printerWidth, + printerNbrLines = this.printerNbrLines, + productNameLength = this.productNameLength, + productWiseReportLimit = this.productWiseReportLimit, + addressWiseReportLimit = this.addressWiseReportLimit, + customerWiseReportLimit = this.customerWiseReportLimit, + printQRCode = this.printQRCode, + printResLogo = this.printResLogo, + printWelcomeText = this.printWelcomeText, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/ProductEntity.kt b/core/database/src/main/java/com/niyaj/database/model/ProductEntity.kt new file mode 100644 index 00000000..065b0b03 --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/ProductEntity.kt @@ -0,0 +1,53 @@ +package com.niyaj.database.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey +import com.niyaj.model.Product +import java.util.Date + +@Entity( + tableName = "product", + foreignKeys = [ForeignKey( + entity = CategoryEntity::class, + parentColumns = arrayOf("categoryId"), + childColumns = arrayOf("categoryId"), + onDelete = ForeignKey.CASCADE + )] +) +data class ProductEntity( + @PrimaryKey(autoGenerate = true) + val productId: Int = 0, + + @ColumnInfo(index = true) + val categoryId: Int = 0, + + val productName: String = "", + + val productPrice: Int = 0, + + val productDescription: String = "", + + val productAvailability: Boolean = true, + + @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") + val createdAt: Date, + + @ColumnInfo(defaultValue = "NULL") + val updatedAt: Date? = null + +) + +fun ProductEntity.asExternalModel(): Product { + return Product( + productId = this.productId, + categoryId = this.categoryId, + productName = this.productName, + productPrice = this.productPrice, + productDescription = this.productDescription, + productAvailability = this.productAvailability, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/core/database/src/main/java/com/niyaj/database/model/ProfileEntity.kt b/core/database/src/main/java/com/niyaj/database/model/ProfileEntity.kt new file mode 100644 index 00000000..85e94cdf --- /dev/null +++ b/core/database/src/main/java/com/niyaj/database/model/ProfileEntity.kt @@ -0,0 +1,54 @@ +package com.niyaj.database.model + +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.niyaj.model.Profile + +@Entity(tableName = "profile") +data class ProfileEntity( + @PrimaryKey + val restaurantId: Int, + + val name: String, + + val email: String, + + val primaryPhone: String, + + val secondaryPhone: String = "", + + val tagline: String, + + val description: String = "", + + val address: String, + + val logo: String, + + val printLogo: String = "", + + val paymentQrCode: String = "", + + val createdAt: String = System.currentTimeMillis().toString(), + + val updatedAt: String? = null, +) + + +fun ProfileEntity.asExternalModel(): Profile { + return Profile( + restaurantId = this.restaurantId, + name = this.name, + email = this.email, + primaryPhone = this.primaryPhone, + secondaryPhone = this.secondaryPhone, + tagline = this.tagline, + description = this.description, + address = this.address, + logo = this.logo, + printLogo = this.printLogo, + paymentQrCode = this.paymentQrCode, + createdAt = this.createdAt, + updatedAt = this.updatedAt + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/selected/domain/model/Selected.kt b/core/database/src/main/java/com/niyaj/database/model/SelectedEntity.kt similarity index 62% rename from app/src/main/java/com/niyaj/poposroom/features/selected/domain/model/Selected.kt rename to core/database/src/main/java/com/niyaj/database/model/SelectedEntity.kt index ba597236..8c62e70f 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/selected/domain/model/Selected.kt +++ b/core/database/src/main/java/com/niyaj/database/model/SelectedEntity.kt @@ -1,11 +1,10 @@ -package com.niyaj.poposroom.features.selected.domain.model +package com.niyaj.database.model import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey import androidx.room.PrimaryKey -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrderEntity -import com.niyaj.poposroom.features.selected.domain.utils.SelectedTestTag.SELECTED_ID +import com.niyaj.model.Selected @Entity( tableName = "selected", @@ -19,11 +18,19 @@ import com.niyaj.poposroom.features.selected.domain.utils.SelectedTestTag.SELECT ) ] ) -data class Selected( +data class SelectedEntity( @PrimaryKey @ColumnInfo(index = true) - val selectedId: String = SELECTED_ID, + val selectedId: String, @ColumnInfo(index = true) - val orderId: Int = 0, + val orderId: Int, ) + + +fun SelectedEntity.asExternalModel(): Selected { + return Selected( + selectedId = this.selectedId, + orderId = this.orderId + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/database/utils/TimestampConverters.kt b/core/database/src/main/java/com/niyaj/database/util/TimestampConverters.kt similarity index 83% rename from app/src/main/java/com/niyaj/poposroom/features/common/database/utils/TimestampConverters.kt rename to core/database/src/main/java/com/niyaj/database/util/TimestampConverters.kt index b5b1fcf9..9d029959 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/database/utils/TimestampConverters.kt +++ b/core/database/src/main/java/com/niyaj/database/util/TimestampConverters.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.database.utils +package com.niyaj.database.util import androidx.room.TypeConverter import java.util.Date diff --git a/core/database/src/test/java/com/niyaj/database/ExampleUnitTest.kt b/core/database/src/test/java/com/niyaj/database/ExampleUnitTest.kt new file mode 100644 index 00000000..820acb8f --- /dev/null +++ b/core/database/src/test/java/com/niyaj/database/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.database + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/core/designsystem/.gitignore b/core/designsystem/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/designsystem/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/designsystem/build.gradle.kts b/core/designsystem/build.gradle.kts new file mode 100644 index 00000000..7dff0b1f --- /dev/null +++ b/core/designsystem/build.gradle.kts @@ -0,0 +1,35 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.library") + id("popos.android.library.compose") + id("popos.android.library.jacoco") +} + +android { + namespace = "com.niyaj.core.designsystem" + + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + lint { + checkDependencies = true + } +} + +dependencies { + api(libs.androidx.compose.foundation) + api(libs.androidx.compose.foundation.layout) + api(libs.androidx.compose.material.iconsExtended) + api(libs.androidx.compose.material3) + api(libs.androidx.compose.runtime) + api(libs.androidx.compose.ui.tooling.preview) + api(libs.androidx.compose.ui.util) + + debugApi(libs.androidx.compose.ui.tooling) + + implementation(libs.androidx.core.ktx) + implementation(libs.coil.kt.compose) + implementation(libs.navigation.bar) + + androidTestImplementation(project(":core:testing")) +} \ No newline at end of file diff --git a/core/designsystem/src/androidTest/java/com/niyaj/designsystem/ExampleInstrumentedTest.kt b/core/designsystem/src/androidTest/java/com/niyaj/designsystem/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..fccf0d81 --- /dev/null +++ b/core/designsystem/src/androidTest/java/com/niyaj/designsystem/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.designsystem + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.designsystem.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/designsystem/src/main/AndroidManifest.xml b/core/designsystem/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/designsystem/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/designsystem/src/main/java/com/niyaj/designsystem/components/Background.kt b/core/designsystem/src/main/java/com/niyaj/designsystem/components/Background.kt new file mode 100644 index 00000000..3496cf5f --- /dev/null +++ b/core/designsystem/src/main/java/com/niyaj/designsystem/components/Background.kt @@ -0,0 +1,180 @@ +package com.niyaj.designsystem.components + +import android.content.res.Configuration +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material3.LocalAbsoluteTonalElevation +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import com.niyaj.designsystem.theme.GradientColors +import com.niyaj.designsystem.theme.LocalBackgroundTheme +import com.niyaj.designsystem.theme.LocalGradientColors +import com.niyaj.designsystem.theme.PoposRoomTheme +import kotlin.math.tan + +/** + * The main background for the app. + * Uses [LocalBackgroundTheme] to set the color and tonal elevation of a [Surface]. + * + * @param modifier Modifier to be applied to the background. + * @param content The background content. + */ +@Composable +fun PoposBackground( + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) { + val color = LocalBackgroundTheme.current.color + val tonalElevation = LocalBackgroundTheme.current.tonalElevation + Surface( + color = if (color == Color.Unspecified) Color.Transparent else color, + tonalElevation = if (tonalElevation == Dp.Unspecified) 0.dp else tonalElevation, + modifier = modifier.fillMaxSize(), + ) { + CompositionLocalProvider(LocalAbsoluteTonalElevation provides 0.dp) { + content() + } + } +} + +/** + * A gradient background for select screens. Uses [LocalBackgroundTheme] to set the gradient colors + * of a [Box] within a [Surface]. + * + * @param modifier Modifier to be applied to the background. + * @param gradientColors The gradient colors to be rendered. + * @param content The background content. + */ +@Composable +fun PoposGradientBackground( + modifier: Modifier = Modifier, + gradientColors: GradientColors = LocalGradientColors.current, + content: @Composable () -> Unit, +) { + val currentTopColor by rememberUpdatedState(gradientColors.top) + val currentBottomColor by rememberUpdatedState(gradientColors.bottom) + Surface( + color = if (gradientColors.container == Color.Unspecified) { + Color.Transparent + } else { + gradientColors.container + }, + modifier = modifier.fillMaxSize(), + ) { + Box( + Modifier + .fillMaxSize() + .drawWithCache { + // Compute the start and end coordinates such that the gradients are angled 11.06 + // degrees off the vertical axis + val offset = size.height * tan( + Math + .toRadians(11.06) + .toFloat(), + ) + + val start = Offset(size.width / 2 + offset / 2, 0f) + val end = Offset(size.width / 2 - offset / 2, size.height) + + // Create the top gradient that fades out after the halfway point vertically + val topGradient = Brush.linearGradient( + 0f to if (currentTopColor == Color.Unspecified) { + Color.Transparent + } else { + currentTopColor + }, + 0.724f to Color.Transparent, + start = start, + end = end, + ) + // Create the bottom gradient that fades in before the halfway point vertically + val bottomGradient = Brush.linearGradient( + 0.2552f to Color.Transparent, + 1f to if (currentBottomColor == Color.Unspecified) { + Color.Transparent + } else { + currentBottomColor + }, + start = start, + end = end, + ) + + onDrawBehind { + // There is overlap here, so order is important + drawRect(topGradient) + drawRect(bottomGradient) + } + }, + ) { + content() + } + } +} + +/** + * Multipreview annotation that represents light and dark themes. Add this annotation to a + * composable to render the both themes. + */ +@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, name = "Light theme") +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, name = "Dark theme") +annotation class ThemePreviews + +@ThemePreviews +@Composable +fun BackgroundDefault() { + PoposRoomTheme(disableDynamicTheming = true) { + PoposBackground(Modifier.size(100.dp), content = {}) + } +} + +@ThemePreviews +@Composable +fun BackgroundDynamic() { + PoposRoomTheme(disableDynamicTheming = false) { + PoposBackground(Modifier.size(100.dp), content = {}) + } +} + +@ThemePreviews +@Composable +fun BackgroundAndroid() { + PoposRoomTheme(androidTheme = true) { + PoposBackground(Modifier.size(100.dp), content = {}) + } +} + +@ThemePreviews +@Composable +fun GradientBackgroundDefault() { + PoposRoomTheme(disableDynamicTheming = true) { + PoposGradientBackground(Modifier.size(100.dp), content = {}) + } +} + +@ThemePreviews +@Composable +fun GradientBackgroundDynamic() { + PoposRoomTheme(disableDynamicTheming = false) { + PoposGradientBackground(Modifier.size(100.dp), content = {}) + } +} + +@ThemePreviews +@Composable +fun GradientBackgroundAndroid() { + PoposRoomTheme(androidTheme = true) { + PoposGradientBackground(Modifier.size(100.dp), content = {}) + } +} diff --git a/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Background.kt b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Background.kt new file mode 100644 index 00000000..b2a9b304 --- /dev/null +++ b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Background.kt @@ -0,0 +1,20 @@ +package com.niyaj.designsystem.theme + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp + +/** + * A class to model background color and tonal elevation values for Now in Android. + */ +@Immutable +data class BackgroundTheme( + val color: Color = Color.Unspecified, + val tonalElevation: Dp = Dp.Unspecified, +) + +/** + * A composition local for [BackgroundTheme]. + */ +val LocalBackgroundTheme = staticCompositionLocalOf { BackgroundTheme() } diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Color.kt b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Color.kt similarity index 97% rename from app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Color.kt rename to core/designsystem/src/main/java/com/niyaj/designsystem/theme/Color.kt index af645b7d..36d42072 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Color.kt +++ b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Color.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.ui.theme +package com.niyaj.designsystem.theme import androidx.compose.ui.graphics.Color val light_primary = Color(0xFF7533DC) @@ -79,6 +79,8 @@ val LightColor9 = Color(0xFFF9F9F9) val LightColor10 = Color(0xFFF9F3F3) val LightColor12 = Color(0xFFF9F9F9) +internal val DarkGreenGray95 = Color(0xFFF0F1EC) + val ElectricViolet = Color(0xFF7D26FE) val Purple = Color(0xFF742DF6) diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Dimensions.kt b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Dimensions.kt similarity index 90% rename from app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Dimensions.kt rename to core/designsystem/src/main/java/com/niyaj/designsystem/theme/Dimensions.kt index feb9dd4b..61706d0c 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Dimensions.kt +++ b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Dimensions.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.ui.theme +package com.niyaj.designsystem.theme import androidx.compose.ui.unit.dp diff --git a/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Gradient.kt b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Gradient.kt new file mode 100644 index 00000000..def58d8c --- /dev/null +++ b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Gradient.kt @@ -0,0 +1,24 @@ +package com.niyaj.designsystem.theme + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color + +/** + * A class to model gradient color values for Now in Android. + * + * @param top The top gradient color to be rendered. + * @param bottom The bottom gradient color to be rendered. + * @param container The container gradient color over which the gradient will be rendered. + */ +@Immutable +data class GradientColors( + val top: Color = Color.Unspecified, + val bottom: Color = Color.Unspecified, + val container: Color = Color.Unspecified, +) + +/** + * A composition local for [GradientColors]. + */ +val LocalGradientColors = staticCompositionLocalOf { GradientColors() } diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Shape.kt b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Shape.kt similarity index 87% rename from app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Shape.kt rename to core/designsystem/src/main/java/com/niyaj/designsystem/theme/Shape.kt index 3fb6106e..0a77180c 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Shape.kt +++ b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Shape.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.ui.theme +package com.niyaj.designsystem.theme import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Shapes diff --git a/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Theme.kt b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Theme.kt new file mode 100644 index 00000000..9f4ba932 --- /dev/null +++ b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Theme.kt @@ -0,0 +1,165 @@ +package com.niyaj.designsystem.theme + +import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.material3.surfaceColorAtElevation +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp + + +private val lightColorScheme = lightColorScheme( + primary = light_primary, + onPrimary = light_onPrimary, + primaryContainer = light_primaryContainer, + onPrimaryContainer = light_onPrimaryContainer, + secondary = light_secondary, + onSecondary = light_onSecondary, + secondaryContainer = light_secondaryContainer, + onSecondaryContainer = light_onSecondaryContainer, + tertiary = light_tertiary, + onTertiary = light_onTertiary, + tertiaryContainer = light_tertiaryContainer, + onTertiaryContainer = light_onTertiaryContainer, + error = light_error, + errorContainer = light_errorContainer, + onError = light_onError, + onErrorContainer = light_onErrorContainer, + background = light_background, + onBackground = light_onBackground, + surface = light_surface, + onSurface = light_onSurface, + surfaceVariant = light_surfaceVariant, + onSurfaceVariant = light_onSurfaceVariant, + outline = light_outline, + inverseOnSurface = light_inverseOnSurface, + inverseSurface = light_inverseSurface, + inversePrimary = light_inversePrimary, + surfaceTint = light_surfaceTint, + outlineVariant = light_outlineVariant, + scrim = light_scrim, +) + + +private val darkColorScheme = darkColorScheme( + primary = dark_primary, + onPrimary = dark_onPrimary, + primaryContainer = dark_primaryContainer, + onPrimaryContainer = dark_onPrimaryContainer, + secondary = dark_secondary, + onSecondary = dark_onSecondary, + secondaryContainer = dark_secondaryContainer, + onSecondaryContainer = dark_onSecondaryContainer, + tertiary = dark_tertiary, + onTertiary = dark_onTertiary, + tertiaryContainer = dark_tertiaryContainer, + onTertiaryContainer = dark_onTertiaryContainer, + error = dark_error, + errorContainer = dark_errorContainer, + onError = dark_onError, + onErrorContainer = dark_onErrorContainer, + background = dark_background, + onBackground = dark_onBackground, + surface = dark_surface, + onSurface = dark_onSurface, + surfaceVariant = dark_surfaceVariant, + onSurfaceVariant = dark_onSurfaceVariant, + outline = dark_outline, + inverseOnSurface = dark_inverseOnSurface, + inverseSurface = dark_inverseSurface, + inversePrimary = dark_inversePrimary, + surfaceTint = dark_surfaceTint, + outlineVariant = dark_outlineVariant, + scrim = dark_scrim, +) + + +/** + * Light Android gradient colors + */ +val LightAndroidGradientColors = GradientColors(container = DarkGreenGray95) + +/** + * Dark Android gradient colors + */ +val DarkAndroidGradientColors = GradientColors(container = Color.Black) + +/** + * Light Android background theme + */ +val LightAndroidBackgroundTheme = BackgroundTheme(color = DarkGreenGray95) + +/** + * Dark Android background theme + */ +val DarkAndroidBackgroundTheme = BackgroundTheme(color = Color.Black) + +@Composable +fun PoposRoomTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + androidTheme: Boolean = false, + disableDynamicTheming: Boolean = true, + content: @Composable () -> Unit, +) { + + // Color scheme + val colorScheme = when { + androidTheme -> if (darkTheme) darkColorScheme else lightColorScheme + !disableDynamicTheming && supportsDynamicTheming() -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + else -> if (darkTheme) darkColorScheme else lightColorScheme + } + // Gradient colors + val emptyGradientColors = GradientColors(container = colorScheme.surfaceColorAtElevation(2.dp)) + val defaultGradientColors = GradientColors( + top = colorScheme.inverseOnSurface, + bottom = colorScheme.primaryContainer, + container = colorScheme.surface, + ) + val gradientColors = when { + androidTheme -> if (darkTheme) DarkAndroidGradientColors else LightAndroidGradientColors + !disableDynamicTheming && supportsDynamicTheming() -> emptyGradientColors + else -> defaultGradientColors + } + // Background theme + val defaultBackgroundTheme = BackgroundTheme( + color = colorScheme.surface, + tonalElevation = 2.dp, + ) + val backgroundTheme = when { + androidTheme -> if (darkTheme) DarkAndroidBackgroundTheme else LightAndroidBackgroundTheme + else -> defaultBackgroundTheme + } + val tintTheme = when { + androidTheme -> TintTheme() + !disableDynamicTheming && supportsDynamicTheming() -> TintTheme(colorScheme.primary) + else -> TintTheme() + } + // Composition locals + CompositionLocalProvider( + LocalGradientColors provides gradientColors, + LocalBackgroundTheme provides backgroundTheme, + LocalTintTheme provides tintTheme, + ) { + MaterialTheme( + colorScheme = colorScheme, + shapes = Shapes, + typography = Typography, + content = content, + ) + } +} + +@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) +fun supportsDynamicTheming() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S diff --git a/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Tint.kt b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Tint.kt new file mode 100644 index 00000000..0873fa36 --- /dev/null +++ b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Tint.kt @@ -0,0 +1,18 @@ +package com.niyaj.designsystem.theme + +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color + +/** + * A class to model background color and tonal elevation values for Now in Android. + */ +@Immutable +data class TintTheme( + val iconTint: Color? = null, +) + +/** + * A composition local for [TintTheme]. + */ +val LocalTintTheme = staticCompositionLocalOf { TintTheme() } diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Type.kt b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Type.kt similarity index 96% rename from app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Type.kt rename to core/designsystem/src/main/java/com/niyaj/designsystem/theme/Type.kt index dc0f0486..46045944 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/ui/theme/Type.kt +++ b/core/designsystem/src/main/java/com/niyaj/designsystem/theme/Type.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.ui.theme +package com.niyaj.designsystem.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle @@ -6,7 +6,7 @@ import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp -import com.niyaj.poposroom.R +import com.niyaj.core.designsystem.R private val fontFamily = FontFamily( @@ -69,7 +69,7 @@ val Typography = Typography( fontWeight = FontWeight.SemiBold, fontSize = 22.sp, lineHeight = 28.sp, - letterSpacing = 0.1.sp, + letterSpacing = 0.9.sp, ), titleMedium = TextStyle( fontFamily = fontFamily, diff --git a/app/src/main/res/font/panton_narrow_black.ttf b/core/designsystem/src/main/res/font/panton_narrow_black.ttf similarity index 100% rename from app/src/main/res/font/panton_narrow_black.ttf rename to core/designsystem/src/main/res/font/panton_narrow_black.ttf diff --git a/app/src/main/res/font/panton_narrow_bold.ttf b/core/designsystem/src/main/res/font/panton_narrow_bold.ttf similarity index 100% rename from app/src/main/res/font/panton_narrow_bold.ttf rename to core/designsystem/src/main/res/font/panton_narrow_bold.ttf diff --git a/app/src/main/res/font/panton_narrow_demo_light.otf b/core/designsystem/src/main/res/font/panton_narrow_demo_light.otf similarity index 100% rename from app/src/main/res/font/panton_narrow_demo_light.otf rename to core/designsystem/src/main/res/font/panton_narrow_demo_light.otf diff --git a/app/src/main/res/font/panton_narrow_extra_bold.ttf b/core/designsystem/src/main/res/font/panton_narrow_extra_bold.ttf similarity index 100% rename from app/src/main/res/font/panton_narrow_extra_bold.ttf rename to core/designsystem/src/main/res/font/panton_narrow_extra_bold.ttf diff --git a/app/src/main/res/font/panton_narrow_light.ttf b/core/designsystem/src/main/res/font/panton_narrow_light.ttf similarity index 100% rename from app/src/main/res/font/panton_narrow_light.ttf rename to core/designsystem/src/main/res/font/panton_narrow_light.ttf diff --git a/app/src/main/res/font/panton_narrow_regular.ttf b/core/designsystem/src/main/res/font/panton_narrow_regular.ttf similarity index 100% rename from app/src/main/res/font/panton_narrow_regular.ttf rename to core/designsystem/src/main/res/font/panton_narrow_regular.ttf diff --git a/app/src/main/res/font/panton_narrow_semi_bold.ttf b/core/designsystem/src/main/res/font/panton_narrow_semi_bold.ttf similarity index 100% rename from app/src/main/res/font/panton_narrow_semi_bold.ttf rename to core/designsystem/src/main/res/font/panton_narrow_semi_bold.ttf diff --git a/core/domain/.gitignore b/core/domain/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/domain/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/domain/build.gradle.kts b/core/domain/build.gradle.kts new file mode 100644 index 00000000..e612abfb --- /dev/null +++ b/core/domain/build.gradle.kts @@ -0,0 +1,22 @@ + +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.library") + id("popos.android.library.jacoco") +} + +android { + namespace = "com.niyaj.core.domain" +} + +dependencies { + implementation(project(":core:data")) + implementation(project(":core:model")) + implementation(project(":core:common")) + implementation(libs.hilt.android) + implementation(libs.kotlinx.coroutines.android) + implementation(libs.kotlinx.datetime) + implementation(libs.pos.printer) + + testImplementation(project(":core:testing")) +} \ No newline at end of file diff --git a/core/domain/src/androidTest/java/com/niyaj/domain/ExampleInstrumentedTest.kt b/core/domain/src/androidTest/java/com/niyaj/domain/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..eb6a3f9e --- /dev/null +++ b/core/domain/src/androidTest/java/com/niyaj/domain/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.domain + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.domain.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/domain/src/main/AndroidManifest.xml b/core/domain/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/domain/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/domain/src/main/java/com/niyaj/domain/utils/BluetoothPrinter.kt b/core/domain/src/main/java/com/niyaj/domain/utils/BluetoothPrinter.kt new file mode 100644 index 00000000..91d3562f --- /dev/null +++ b/core/domain/src/main/java/com/niyaj/domain/utils/BluetoothPrinter.kt @@ -0,0 +1,121 @@ +package com.niyaj.domain.utils + +import android.annotation.SuppressLint +import android.util.Log +import com.dantsu.escposprinter.EscPosPrinter +import com.dantsu.escposprinter.connection.bluetooth.BluetoothConnection +import com.dantsu.escposprinter.connection.bluetooth.BluetoothPrintersConnections +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.repository.PrinterRepository +import com.niyaj.model.BluetoothDeviceState +import com.niyaj.model.Printer +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import javax.inject.Inject + +/** + * BluetoothPrinter is a class that provides functionality for connecting to and interacting with a Bluetooth printer. + * It allows retrieving available printers, connecting to specific printers, and printing data. + * + * @param printerRepository The repository for retrieving printer information. + */ +class BluetoothPrinter @Inject constructor( + private val printerRepository: PrinterRepository, + @Dispatcher(PoposDispatchers.IO) + private val ioDispatcher: CoroutineDispatcher, +) { + + private val _info = MutableStateFlow(Printer()) + val info = _info.asStateFlow() + + private val _printer = MutableStateFlow(null) + val printer = _printer.asStateFlow() + + private val _bluetoothConnection = MutableStateFlow(null) + + private val bluetoothConnection: BluetoothConnection? + get() = _bluetoothConnection.value + + private val connections = BluetoothPrintersConnections() + + init { + runBlocking(ioDispatcher) { + getPrinterInfo() + } + + connectBluetoothPrinter() + } + + @SuppressLint("MissingPermission") + fun getBluetoothPrinters(): Flow> { + return channelFlow { + try { + val list = connections.list?.map { + BluetoothDeviceState( + name = it.device.name, + address = it.device.address, + bondState = it.device.bondState, + type = it.device.type, + connected = it.isConnected, + ) + } + + list?.let { + send(it) + } + } catch (e: Exception) { + send(emptyList()) + } + } + } + + fun connectBluetoothPrinter(address: String) { + try { + val device = connections.list?.find { it.device.address == address } + device?.connect() + + _bluetoothConnection.value = device + _printer.value = EscPosPrinter( + device, + _info.value.printerDpi, + _info.value.printerWidth, + _info.value.printerNbrLines + ) + } catch (e: Exception) { + Log.e("Bluetooth", "Failed to connect") + } + } + + private fun connectBluetoothPrinter() { + try { + val data = BluetoothPrintersConnections.selectFirstPaired() + data?.connect() + + _bluetoothConnection.value = data + _printer.value = EscPosPrinter( + data, + _info.value.printerDpi, + _info.value.printerWidth, + _info.value.printerNbrLines + ) + } catch (e: Exception) { + Log.e("Bluetooth", "Failed to connect") + } + } + + fun printTestData() { + _printer.value?.printFormattedText("[C]Testing \n") + } + + private suspend fun getPrinterInfo() { + withContext(ioDispatcher) { + _info.value = printerRepository.getPrinter() + } + } +} diff --git a/core/domain/src/test/java/com/niyaj/domain/ExampleUnitTest.kt b/core/domain/src/test/java/com/niyaj/domain/ExampleUnitTest.kt new file mode 100644 index 00000000..ffa0928f --- /dev/null +++ b/core/domain/src/test/java/com/niyaj/domain/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.domain + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/core/model/.gitignore b/core/model/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/model/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts new file mode 100644 index 00000000..c2529132 --- /dev/null +++ b/core/model/build.gradle.kts @@ -0,0 +1,17 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.library") +// id("kotlin") +} + +android { + namespace = "com.niyaj.core.model" + compileSdk = libs.versions.compileSdk.get().toInt() +} + +dependencies { + implementation(project(":core:common")) + implementation(libs.androidx.core.ktx) + implementation(libs.kotlinx.coroutines.android) + implementation(libs.kotlinx.serialization.json) +} \ No newline at end of file diff --git a/core/model/src/androidTest/java/com/niyaj/model/ExampleInstrumentedTest.kt b/core/model/src/androidTest/java/com/niyaj/model/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..003f4dc5 --- /dev/null +++ b/core/model/src/androidTest/java/com/niyaj/model/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.model + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.model.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/model/src/main/AndroidManifest.xml b/core/model/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/model/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/Absent.kt b/core/model/src/main/java/com/niyaj/model/Absent.kt new file mode 100644 index 00000000..307abbf1 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/Absent.kt @@ -0,0 +1,26 @@ +package com.niyaj.model + +import com.niyaj.common.utils.toJoinedDate +import java.util.Date + +data class Absent( + val absentId: Int = 0, + + val employeeId: Int, + + val absentReason: String = "", + + val absentDate: String = "", + + val createdAt: Date, + + val updatedAt: Date? = null, +) + +/** + * Filter absent employee by date and absent reason + */ +fun Absent.filterAbsent(searchText: String): Boolean { + return this.absentDate.toJoinedDate.contains(searchText, true) || + this.absentReason.contains(searchText, true) +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/model/AddOnItem.kt b/core/model/src/main/java/com/niyaj/model/AddOnItem.kt similarity index 52% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/model/AddOnItem.kt rename to core/model/src/main/java/com/niyaj/model/AddOnItem.kt index cc188ff0..07eb288f 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/domain/model/AddOnItem.kt +++ b/core/model/src/main/java/com/niyaj/model/AddOnItem.kt @@ -1,14 +1,8 @@ -package com.niyaj.poposroom.features.addon_item.domain.model +package com.niyaj.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey import java.util.Date -@Entity(tableName = "addonitem") data class AddOnItem( - @PrimaryKey(autoGenerate = true) - @ColumnInfo(index = true) val itemId: Int, val itemName: String, @@ -17,13 +11,13 @@ data class AddOnItem( val isApplicable: Boolean, - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date, - @ColumnInfo(defaultValue = "NULL") val updatedAt: Date? = null, ) +data class AddOnPriceWithApplicable(val itemPrice: Int, val isApplicable: Boolean) + fun List.searchAddOnItem(searchText: String): List { return if (searchText.isNotEmpty()) { @@ -31,7 +25,5 @@ fun List.searchAddOnItem(searchText: String): List { it.itemName.contains(searchText, true) || it.itemPrice.toString().contains(searchText, true) } - }else this -} - -data class AddOnPriceWithApplicable(var itemPrice: Int, var isApplicable: Boolean) \ No newline at end of file + } else this +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/domain/model/Address.kt b/core/model/src/main/java/com/niyaj/model/Address.kt similarity index 59% rename from app/src/main/java/com/niyaj/poposroom/features/address/domain/model/Address.kt rename to core/model/src/main/java/com/niyaj/model/Address.kt index edffcfed..af2fc830 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/domain/model/Address.kt +++ b/core/model/src/main/java/com/niyaj/model/Address.kt @@ -1,32 +1,24 @@ -package com.niyaj.poposroom.features.address.domain.model +package com.niyaj.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey import java.util.Date -@Entity(tableName = "address") data class Address( - @PrimaryKey(autoGenerate = true) val addressId: Int = 0, val addressName: String = "", val shortName: String = "", - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date = Date(), - @ColumnInfo(defaultValue = "NULL") val updatedAt: Date? = null, ) - fun List
.searchAddress(searchText: String): List
{ return if (searchText.isNotEmpty()) { this.filter { it.addressName.contains(searchText, true) || it.shortName.toString().contains(searchText, true) } - }else this + } else this } diff --git a/core/model/src/main/java/com/niyaj/model/BluetoothDeviceState.kt b/core/model/src/main/java/com/niyaj/model/BluetoothDeviceState.kt new file mode 100644 index 00000000..dcce57f1 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/BluetoothDeviceState.kt @@ -0,0 +1,9 @@ +package com.niyaj.model + +data class BluetoothDeviceState( + val name: String = "", + val address: String = "", + val bondState: Int? = null, + val type: Int? = null, + val connected: Boolean = false, +) diff --git a/core/model/src/main/java/com/niyaj/model/Cart.kt b/core/model/src/main/java/com/niyaj/model/Cart.kt new file mode 100644 index 00000000..1d036c63 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/Cart.kt @@ -0,0 +1,25 @@ +package com.niyaj.model + +import java.util.Date + +data class Cart( + val cartId: Int = 0, + + val orderId: Int = 0, + + val productId: Int = 0, + + val quantity: Int = 0, + + val createdAt: Date = Date(), + + val updatedAt: Date? = null, +) + + +data class ProductPriceWithQuantity( + + val productPrice: Int, + + val quantity: Int +) \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/CartAddOnItems.kt b/core/model/src/main/java/com/niyaj/model/CartAddOnItems.kt new file mode 100644 index 00000000..f363be53 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/CartAddOnItems.kt @@ -0,0 +1,23 @@ +package com.niyaj.model + +import java.util.Date + +data class CartAddOnItems( + val orderId: Int, + + val itemId: Int, + + val createdAt: Date = Date(), +) + +data class CartOrderWithAddOnItemsId( + val cartOrderEntity: CartOrder, + + val items: List = emptyList() +) + +data class CartOrderWithAddOnItemsPrice( + val cartOrderEntity: CartOrder, + + val items: List = emptyList() +) \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/CartCharges.kt b/core/model/src/main/java/com/niyaj/model/CartCharges.kt new file mode 100644 index 00000000..ea32c94e --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/CartCharges.kt @@ -0,0 +1,24 @@ +package com.niyaj.model + +import java.util.Date + + +data class CartCharges( + val orderId: Int, + + val chargesId: Int, + + val createdAt: Date = Date(), +) + +data class CartOrderWithChargesId( + val cartOrderEntity: CartOrder, + + val items: List = emptyList() +) + +data class CartOrderWithChargesPrice( + val cartOrderEntity: CartOrder, + + val items: List = emptyList() +) \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/CartItem.kt b/core/model/src/main/java/com/niyaj/model/CartItem.kt similarity index 79% rename from app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/CartItem.kt rename to core/model/src/main/java/com/niyaj/model/CartItem.kt index a688880f..f9191229 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/CartItem.kt +++ b/core/model/src/main/java/com/niyaj/model/CartItem.kt @@ -1,6 +1,5 @@ -package com.niyaj.poposroom.features.cart.domain.model +package com.niyaj.model -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flowOf @@ -14,7 +13,7 @@ data class CartItem( val customerPhone: String? = null, val customerAddress: String? = null, val updatedAt: String = "", - val orderPrice : Flow = flowOf(OrderPrice()), + val orderPrice: Flow = flowOf(OrderPrice()), ) data class OrderPrice( @@ -32,5 +31,5 @@ data class CartItems( val customerPhone: String? = null, val customerAddress: String? = null, val updatedAt: String = "", - val orderPrice : OrderPrice = OrderPrice(), + val orderPrice: OrderPrice = OrderPrice(), ) \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/CartOrder.kt b/core/model/src/main/java/com/niyaj/model/CartOrder.kt new file mode 100644 index 00000000..af3b3f80 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/CartOrder.kt @@ -0,0 +1,40 @@ +package com.niyaj.model + +import com.niyaj.common.utils.toTime +import java.util.Date + + +data class CartOrder( + val orderId: Int = 0, + + val orderType: OrderType = OrderType.DineIn, + + val orderStatus: OrderStatus = OrderStatus.PROCESSING, + + val doesChargesIncluded: Boolean = false, + + val customer: Customer = Customer(), + + val address: Address = Address(), + + val createdAt: Date = Date(), + + val updatedAt: Date? = null, +) + + +fun List.filterCartOrder(searchText: String): List { + return if (searchText.isNotEmpty()) { + this.filter { cartOrder -> + cartOrder.orderStatus.name.contains(searchText, true) || + cartOrder.customer.customerPhone.contains(searchText, true) || + cartOrder.customer.customerName?.contains(searchText, true) == true || + cartOrder.address.addressName.contains(searchText, true) || + cartOrder.address.shortName.contains(searchText, true) || + cartOrder.orderType.name.contains(searchText, true) || + cartOrder.orderId.toString().contains(searchText, true) || + cartOrder.createdAt.toTime.contains(searchText, true) || + cartOrder.updatedAt?.toTime?.contains(searchText, true) == true + } + } else this +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/CartProductItem.kt b/core/model/src/main/java/com/niyaj/model/CartProductItem.kt similarity index 74% rename from app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/CartProductItem.kt rename to core/model/src/main/java/com/niyaj/model/CartProductItem.kt index 41568096..781617bb 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/domain/model/CartProductItem.kt +++ b/core/model/src/main/java/com/niyaj/model/CartProductItem.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart.domain.model +package com.niyaj.model data class CartProductItem( val productId: Int = 0, diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/domain/model/Category.kt b/core/model/src/main/java/com/niyaj/model/Category.kt similarity index 66% rename from app/src/main/java/com/niyaj/poposroom/features/category/domain/model/Category.kt rename to core/model/src/main/java/com/niyaj/model/Category.kt index 63c0f6df..b5aede55 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/domain/model/Category.kt +++ b/core/model/src/main/java/com/niyaj/model/Category.kt @@ -1,23 +1,16 @@ -package com.niyaj.poposroom.features.category.domain.model +package com.niyaj.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey import java.util.Date -@Entity(tableName = "category") data class Category( - @PrimaryKey(autoGenerate = true) val categoryId: Int = 0, val categoryName: String = "", val isAvailable: Boolean = true, - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date = Date(), - @ColumnInfo(defaultValue = "NULL") val updatedAt: Date? = null, ) diff --git a/core/model/src/main/java/com/niyaj/model/CategoryWithProduct.kt b/core/model/src/main/java/com/niyaj/model/CategoryWithProduct.kt new file mode 100644 index 00000000..b5d7bef8 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/CategoryWithProduct.kt @@ -0,0 +1,15 @@ +package com.niyaj.model + + +data class CategoryWithProduct( + val categoryId: Int, + + val productId: Int +) + + +data class CategoryWithProducts( + val category: Category, + + val products: List = emptyList() +) \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/domain/model/Charges.kt b/core/model/src/main/java/com/niyaj/model/Charges.kt similarity index 55% rename from app/src/main/java/com/niyaj/poposroom/features/charges/domain/model/Charges.kt rename to core/model/src/main/java/com/niyaj/model/Charges.kt index fd33f23c..f0bea220 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/domain/model/Charges.kt +++ b/core/model/src/main/java/com/niyaj/model/Charges.kt @@ -1,14 +1,8 @@ -package com.niyaj.poposroom.features.charges.domain.model +package com.niyaj.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey import java.util.Date -@Entity(tableName = "charges") data class Charges( - @PrimaryKey(autoGenerate = true) - @ColumnInfo(index = true) val chargesId: Int, val chargesName: String, @@ -17,10 +11,8 @@ data class Charges( val isApplicable: Boolean, - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date, - @ColumnInfo(defaultValue = "NULL") val updatedAt: Date? = null, ) @@ -31,7 +23,7 @@ fun List.searchCharges(searchText: String): List { it.chargesName.contains(searchText, true) || it.chargesPrice.toString().contains(searchText, true) } - }else this + } else this } -data class ChargesPriceWithApplicable(var chargesPrice: Int, var isApplicable: Boolean) \ No newline at end of file +data class ChargesPriceWithApplicable(val chargesPrice: Int, val isApplicable: Boolean) \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/domain/model/Customer.kt b/core/model/src/main/java/com/niyaj/model/Customer.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/customer/domain/model/Customer.kt rename to core/model/src/main/java/com/niyaj/model/Customer.kt index 0f900b61..3e01003a 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/domain/model/Customer.kt +++ b/core/model/src/main/java/com/niyaj/model/Customer.kt @@ -1,25 +1,18 @@ -package com.niyaj.poposroom.features.customer.domain.model +package com.niyaj.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey import java.util.Date -@Entity(tableName = "customer") data class Customer( - @PrimaryKey(autoGenerate = true) val customerId: Int = 0, - val customerName: String? = null, - val customerPhone: String = "", + val customerName: String? = null, + val customerEmail: String? = null, - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date = Date(), - @ColumnInfo(defaultValue = "NULL") val updatedAt: Date? = null, ) @@ -31,5 +24,5 @@ fun List.searchCustomer(searchText: String): List { it.customerEmail.toString().contains(searchText, true) || it.customerName.toString().contains(searchText, true) } - }else this + } else this } \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/DarkThemeConfig.kt b/core/model/src/main/java/com/niyaj/model/DarkThemeConfig.kt new file mode 100644 index 00000000..394c9756 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/DarkThemeConfig.kt @@ -0,0 +1,5 @@ +package com.niyaj.model + +enum class DarkThemeConfig { + FOLLOW_SYSTEM, LIGHT, DARK +} diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/model/Employee.kt b/core/model/src/main/java/com/niyaj/model/Employee.kt similarity index 57% rename from app/src/main/java/com/niyaj/poposroom/features/employee/domain/model/Employee.kt rename to core/model/src/main/java/com/niyaj/model/Employee.kt index f957d2d4..b785cbad 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/model/Employee.kt +++ b/core/model/src/main/java/com/niyaj/model/Employee.kt @@ -1,16 +1,8 @@ -package com.niyaj.poposroom.features.employee.domain.model +package com.niyaj.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeSalaryType -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeType import java.util.Date -@Entity(tableName = "employee") data class Employee( - @PrimaryKey(autoGenerate = true) - @ColumnInfo(index = true) val employeeId: Int = 0, val employeeName: String = "", @@ -29,10 +21,8 @@ data class Employee( val employeeType: EmployeeType = EmployeeType.FullTime, - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date = Date(), - @ColumnInfo(defaultValue = "NULL") val updatedAt: Date? = null, ) @@ -40,13 +30,13 @@ fun List.searchEmployee(searchText: String): List { return if (searchText.isNotEmpty()) { this.filter { it.employeeName.contains(searchText, true) || - it.employeePosition.contains(searchText, true) || - it.employeePhone.contains(searchText, true) || - it.employeeSalaryType.name.contains(searchText, true) || - it.employeeSalary.contains(searchText, true) || - it.employeeJoinedDate.contains(searchText, true) + it.employeePosition.contains(searchText, true) || + it.employeePhone.contains(searchText, true) || + it.employeeSalaryType.name.contains(searchText, true) || + it.employeeSalary.contains(searchText, true) || + it.employeeJoinedDate.contains(searchText, true) } - }else this + } else this } /** @@ -54,7 +44,7 @@ fun List.searchEmployee(searchText: String): List { * @param searchText String * @return Boolean */ -fun Employee.filterEmployee(searchText: String) : Boolean { +fun Employee.filterEmployee(searchText: String): Boolean { return this.employeeName.contains(searchText, true) || this.employeePosition.contains(searchText, true) || this.employeePhone.contains(searchText, true) || diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/utils/EmployeeEnums.kt b/core/model/src/main/java/com/niyaj/model/EmployeeEnums.kt similarity index 80% rename from app/src/main/java/com/niyaj/poposroom/features/employee/domain/utils/EmployeeEnums.kt rename to core/model/src/main/java/com/niyaj/model/EmployeeEnums.kt index 605dd5cd..9cb31993 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/domain/utils/EmployeeEnums.kt +++ b/core/model/src/main/java/com/niyaj/model/EmployeeEnums.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee.domain.utils +package com.niyaj.model enum class EmployeeSalaryType { diff --git a/core/model/src/main/java/com/niyaj/model/EmployeeWithAbsent.kt b/core/model/src/main/java/com/niyaj/model/EmployeeWithAbsent.kt new file mode 100644 index 00000000..c34779c0 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/EmployeeWithAbsent.kt @@ -0,0 +1,14 @@ +package com.niyaj.model + +data class EmployeeWithAbsent( + val employeeId: Int, + + val absentId: Int +) + + +data class EmployeeWithAbsents( + val employee: Employee, + + val absents: List = emptyList() +) \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/EmployeeWithPayment.kt b/core/model/src/main/java/com/niyaj/model/EmployeeWithPayment.kt new file mode 100644 index 00000000..f3b1a163 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/EmployeeWithPayment.kt @@ -0,0 +1,23 @@ +package com.niyaj.model + +data class EmployeeWithPayment( + val employeeId: Int, + + val paymentId: Int +) + +data class EmployeeWithPayments( + val employee: Employee, + + val payments: List = emptyList() +) + + +fun List.searchEmployeeWithPayments(searchText: String): List { + return if (searchText.isNotEmpty()) { + this.filter { withSalary -> + withSalary.employee.filterEmployee(searchText) || + withSalary.payments.any { it.filterPayment(searchText) } + } + } else this +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/domain/model/Expense.kt b/core/model/src/main/java/com/niyaj/model/Expense.kt similarity index 63% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/domain/model/Expense.kt rename to core/model/src/main/java/com/niyaj/model/Expense.kt index 4a56b8e8..4b6bd794 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/domain/model/Expense.kt +++ b/core/model/src/main/java/com/niyaj/model/Expense.kt @@ -1,14 +1,9 @@ -package com.niyaj.poposroom.features.expenses.domain.model +package com.niyaj.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import com.niyaj.poposroom.features.common.utils.toJoinedDate +import com.niyaj.common.utils.toJoinedDate import java.util.Date -@Entity data class Expense( - @PrimaryKey(autoGenerate = true) val expenseId: Int = 0, val expenseName: String = "", @@ -19,10 +14,8 @@ data class Expense( val expenseNote: String = "", - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date = Date(), - @ColumnInfo(defaultValue = "NULL") val updatedAt: Date? = null, ) @@ -34,5 +27,5 @@ fun List.searchExpense(searchText: String): List { it.expenseName.contains(searchText, true) || it.expenseDate.toJoinedDate.contains(searchText, true) } - }else this + } else this } \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/Order.kt b/core/model/src/main/java/com/niyaj/model/Order.kt new file mode 100644 index 00000000..daa71a8c --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/Order.kt @@ -0,0 +1,28 @@ +package com.niyaj.model + +import com.niyaj.common.utils.toTime +import java.util.Date + +data class Order( + val orderId: Int = 0, + val orderType: OrderType = OrderType.DineIn, + val customerPhone: String? = null, + val customerAddress: String? = null, + val orderDate: Date = Date(), + val orderPrice: OrderPrice = OrderPrice() +) + + +fun List.searchOrder(searchText: String): List { + return if (searchText.isNotEmpty()) { + this.filter { + it.orderId.toString().contains(searchText, true) || + it.orderType.name.contains(searchText, true) || + it.customerPhone?.contains(searchText, true) == true || + it.customerAddress?.contains(searchText, true) == true || + it.orderDate.toTime.contains(searchText, true) || + it.orderPrice.totalPrice.plus(it.orderPrice.discountPrice).toString() + .contains(searchText, true) + } + } else this +} \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/OrderDetails.kt b/core/model/src/main/java/com/niyaj/model/OrderDetails.kt new file mode 100644 index 00000000..0c447d2c --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/OrderDetails.kt @@ -0,0 +1,12 @@ +package com.niyaj.model + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.emptyFlow + +data class OrderDetails( + val cartOrder: CartOrder = CartOrder(), + val cartProducts: List = emptyList(), + val addOnItems: Flow> = emptyFlow(), + val charges: Flow> = emptyFlow(), + val orderPrice: OrderPrice = OrderPrice() +) diff --git a/core/model/src/main/java/com/niyaj/model/OrderStatus.kt b/core/model/src/main/java/com/niyaj/model/OrderStatus.kt new file mode 100644 index 00000000..070861ab --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/OrderStatus.kt @@ -0,0 +1,6 @@ +package com.niyaj.model + +enum class OrderStatus { + PROCESSING, + PLACED +} \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/OrderType.kt b/core/model/src/main/java/com/niyaj/model/OrderType.kt new file mode 100644 index 00000000..3b60091e --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/OrderType.kt @@ -0,0 +1,6 @@ +package com.niyaj.model + +enum class OrderType { + DineIn, + DineOut, +} \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/OrderWithCartItems.kt b/core/model/src/main/java/com/niyaj/model/OrderWithCartItems.kt new file mode 100644 index 00000000..3c12419d --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/OrderWithCartItems.kt @@ -0,0 +1,10 @@ +package com.niyaj.model + +data class OrderWithCartItems( + val cartOrder: CartOrder, + + val cartItems: List, +) + + +data class ProductAndQuantity(val productId: Int, val quantity: Int) \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/model/Payment.kt b/core/model/src/main/java/com/niyaj/model/Payment.kt similarity index 62% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/model/Payment.kt rename to core/model/src/main/java/com/niyaj/model/Payment.kt index 7149dcba..52de08b6 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/model/Payment.kt +++ b/core/model/src/main/java/com/niyaj/model/Payment.kt @@ -1,29 +1,11 @@ -package com.niyaj.poposroom.features.employee_payment.domain.model +package com.niyaj.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.PrimaryKey -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee.domain.utils.PaymentMode -import com.niyaj.poposroom.features.employee.domain.utils.PaymentType import java.util.Date -@Entity( - tableName = "payment", - foreignKeys = [ForeignKey( - entity = Employee::class, - parentColumns = arrayOf("employeeId"), - childColumns = arrayOf("employeeId"), - onDelete = ForeignKey.CASCADE - )] -) + data class Payment( - @PrimaryKey(autoGenerate = true) - @ColumnInfo(index = true) val paymentId: Int, - @ColumnInfo(index = true) val employeeId: Int, val paymentAmount: String = "", @@ -36,10 +18,8 @@ data class Payment( val paymentNote: String = "", - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date, - @ColumnInfo(defaultValue = "NULL") val updatedAt: Date? = null, ) diff --git a/core/model/src/main/java/com/niyaj/model/Printer.kt b/core/model/src/main/java/com/niyaj/model/Printer.kt new file mode 100644 index 00000000..403b75ba --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/Printer.kt @@ -0,0 +1,29 @@ +package com.niyaj.model + +data class Printer( + val printerId: String = "", + + val printerDpi: Int = 0, + + val printerWidth: Float = 0f, + + val printerNbrLines: Int = 0, + + val productNameLength: Int = 0, + + val productWiseReportLimit: Int = 0, + + val addressWiseReportLimit: Int = 0, + + val customerWiseReportLimit: Int = 0, + + val printQRCode: Boolean = true, + + val printResLogo: Boolean = true, + + val printWelcomeText: Boolean = true, + + val createdAt: String = "", + + val updatedAt: String? = null, +) diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/domain/model/Product.kt b/core/model/src/main/java/com/niyaj/model/Product.kt similarity index 60% rename from app/src/main/java/com/niyaj/poposroom/features/product/domain/model/Product.kt rename to core/model/src/main/java/com/niyaj/model/Product.kt index 36be8320..fa1b9d31 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/domain/model/Product.kt +++ b/core/model/src/main/java/com/niyaj/model/Product.kt @@ -1,27 +1,11 @@ -package com.niyaj.poposroom.features.product.domain.model +package com.niyaj.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.PrimaryKey -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.common.utils.getAllCapitalizedLetters +import com.niyaj.common.utils.getAllCapitalizedLetters import java.util.Date -@Entity( - tableName = "product", - foreignKeys = [ForeignKey( - entity = Category::class, - parentColumns = arrayOf("categoryId"), - childColumns = arrayOf("categoryId"), - onDelete = ForeignKey.CASCADE - )] -) data class Product( - @PrimaryKey(autoGenerate = true) val productId: Int = 0, - @ColumnInfo(index = true) val categoryId: Int = 0, val productName: String = "", @@ -32,10 +16,8 @@ data class Product( val productAvailability : Boolean = true, - @ColumnInfo(defaultValue = "CURRENT_TIMESTAMP") val createdAt: Date, - @ColumnInfo(defaultValue = "NULL") val updatedAt: Date? = null ) diff --git a/app/src/main/java/com/niyaj/poposroom/features/main_feed/domain/model/ProductWithFlowQuantity.kt b/core/model/src/main/java/com/niyaj/model/ProductWithFlowQuantity.kt similarity index 72% rename from app/src/main/java/com/niyaj/poposroom/features/main_feed/domain/model/ProductWithFlowQuantity.kt rename to core/model/src/main/java/com/niyaj/model/ProductWithFlowQuantity.kt index 639ac7c3..1f0a362b 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/main_feed/domain/model/ProductWithFlowQuantity.kt +++ b/core/model/src/main/java/com/niyaj/model/ProductWithFlowQuantity.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.main_feed.domain.model +package com.niyaj.model -import com.niyaj.poposroom.features.common.utils.getAllCapitalizedLetters +import com.niyaj.common.utils.getAllCapitalizedLetters import kotlinx.coroutines.flow.Flow data class ProductWithFlowQuantity( @@ -11,10 +11,10 @@ data class ProductWithFlowQuantity( val quantity: Flow ) -fun ProductWithFlowQuantity.filterByCategory(categoryId : Int): Boolean { +fun ProductWithFlowQuantity.filterByCategory(categoryId: Int): Boolean { return if (categoryId != 0) { this.categoryId == categoryId - }else true + } else true } fun ProductWithFlowQuantity.filterBySearch(searchText: String): Boolean { @@ -22,5 +22,5 @@ fun ProductWithFlowQuantity.filterBySearch(searchText: String): Boolean { this.productName.contains(searchText, true) || this.productPrice.toString().contains(searchText, true) || getAllCapitalizedLetters(this.productName).contains(searchText, true) - }else true + } else true } \ No newline at end of file diff --git a/core/model/src/main/java/com/niyaj/model/Profile.kt b/core/model/src/main/java/com/niyaj/model/Profile.kt new file mode 100644 index 00000000..534c9977 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/Profile.kt @@ -0,0 +1,29 @@ +package com.niyaj.model + +data class Profile( + val restaurantId: Int, + + val name: String, + + val email: String, + + val primaryPhone: String, + + val secondaryPhone: String = "", + + val tagline: String, + + val description: String = "", + + val address: String, + + val logo: String, + + val printLogo: String = "", + + val paymentQrCode: String = "", + + val createdAt: String = System.currentTimeMillis().toString(), + + val updatedAt: String? = null, +) diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/model/Salary.kt b/core/model/src/main/java/com/niyaj/model/Salary.kt similarity index 89% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/model/Salary.kt rename to core/model/src/main/java/com/niyaj/model/Salary.kt index 57be210d..d424a760 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/domain/model/Salary.kt +++ b/core/model/src/main/java/com/niyaj/model/Salary.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_payment.domain.model +package com.niyaj.model data class SalaryCalculation( val startDate: String = "", diff --git a/core/model/src/main/java/com/niyaj/model/Selected.kt b/core/model/src/main/java/com/niyaj/model/Selected.kt new file mode 100644 index 00000000..55a4ac49 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/Selected.kt @@ -0,0 +1,9 @@ +package com.niyaj.model + +const val SELECTED_ID = "33333333" + +data class Selected( + val selectedId: String = SELECTED_ID, + + val orderId: Int = 0, +) diff --git a/core/model/src/main/java/com/niyaj/model/ThemeBrand.kt b/core/model/src/main/java/com/niyaj/model/ThemeBrand.kt new file mode 100644 index 00000000..49e923d6 --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/ThemeBrand.kt @@ -0,0 +1,5 @@ +package com.niyaj.model + +enum class ThemeBrand { + DEFAULT, ANDROID +} diff --git a/core/model/src/main/java/com/niyaj/model/UserData.kt b/core/model/src/main/java/com/niyaj/model/UserData.kt new file mode 100644 index 00000000..b493ac5d --- /dev/null +++ b/core/model/src/main/java/com/niyaj/model/UserData.kt @@ -0,0 +1,11 @@ +package com.niyaj.model + +/** + * Class summarizing user interest data + */ +data class UserData( + val themeBrand: ThemeBrand = ThemeBrand.DEFAULT, + val darkThemeConfig: DarkThemeConfig = DarkThemeConfig.FOLLOW_SYSTEM, + val useDynamicColor: Boolean = false, + val shouldHideOnboarding: Boolean = false, +) diff --git a/core/model/src/test/java/com/niyaj/model/ExampleUnitTest.kt b/core/model/src/test/java/com/niyaj/model/ExampleUnitTest.kt new file mode 100644 index 00000000..d4da8840 --- /dev/null +++ b/core/model/src/test/java/com/niyaj/model/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.model + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/core/testing/.gitignore b/core/testing/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/testing/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/testing/build.gradle.kts b/core/testing/build.gradle.kts new file mode 100644 index 00000000..6f99d054 --- /dev/null +++ b/core/testing/build.gradle.kts @@ -0,0 +1,29 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.library") + id("popos.android.library.compose") + id("popos.android.hilt") +} + +android { + namespace = "com.niyaj.core.testing" +} + +dependencies { + api(libs.androidx.compose.ui.test) + api(libs.androidx.test.core) + api(libs.androidx.test.espresso.core) + api(libs.androidx.test.rules) + api(libs.androidx.test.runner) + api(libs.hilt.android.testing) + api(libs.junit4) + api(libs.kotlinx.coroutines.test) + api(libs.turbine) + + debugApi(libs.androidx.compose.ui.testManifest) + + implementation(project(":core:common")) + implementation(project(":core:data")) + implementation(project(":core:domain")) + implementation(project(":core:model")) +} \ No newline at end of file diff --git a/core/testing/src/androidTest/java/com/niyaj/testing/ExampleInstrumentedTest.kt b/core/testing/src/androidTest/java/com/niyaj/testing/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..a18021c9 --- /dev/null +++ b/core/testing/src/androidTest/java/com/niyaj/testing/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.testing + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.testing.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/testing/src/main/AndroidManifest.xml b/core/testing/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/testing/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/testing/src/main/java/com/niyaj/testing/PoposTestRunner.kt b/core/testing/src/main/java/com/niyaj/testing/PoposTestRunner.kt new file mode 100644 index 00000000..c397a49a --- /dev/null +++ b/core/testing/src/main/java/com/niyaj/testing/PoposTestRunner.kt @@ -0,0 +1,12 @@ +package com.niyaj.testing + +import android.app.Application +import android.content.Context +import androidx.test.runner.AndroidJUnitRunner +import dagger.hilt.android.testing.HiltTestApplication + +class PoposTestRunner : AndroidJUnitRunner() { + override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application { + return super.newApplication(cl, HiltTestApplication::class.java.name, context) + } +} \ No newline at end of file diff --git a/core/testing/src/test/java/com/niyaj/testing/ExampleUnitTest.kt b/core/testing/src/test/java/com/niyaj/testing/ExampleUnitTest.kt new file mode 100644 index 00000000..d9aa6e67 --- /dev/null +++ b/core/testing/src/test/java/com/niyaj/testing/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.testing + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/core/ui/.gitignore b/core/ui/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/ui/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts new file mode 100644 index 00000000..a3b5f5bf --- /dev/null +++ b/core/ui/build.gradle.kts @@ -0,0 +1,47 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.library") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") +} + +android { + namespace = "com.niyaj.core.ui" + + defaultConfig { + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } +} + +dependencies { + api(libs.androidx.compose.foundation) + api(libs.androidx.compose.foundation.layout) + api(libs.androidx.compose.material.iconsExtended) + api(libs.androidx.compose.material3) + api(libs.androidx.compose.runtime) + api(libs.androidx.compose.runtime.livedata) + api(libs.androidx.compose.ui.tooling.preview) + api(libs.androidx.compose.ui.util) + api(libs.androidx.compose.ui) + api(libs.androidx.compose.ui.graphics) + api(libs.androidx.metrics) + api(libs.androidx.tracing.ktx) + + debugApi(libs.androidx.compose.ui.tooling) + + implementation(project(":core:designsystem")) + implementation(project(":core:domain")) + implementation(project(":core:model")) + implementation(project(":core:common")) + implementation(libs.androidx.hilt.navigation.compose) + implementation(libs.androidx.navigation.compose) + implementation(libs.accompanist.systemuicontroller) + implementation(libs.androidx.core.ktx) + implementation(libs.coil.kt) + implementation(libs.coil.kt.compose) + implementation(libs.kotlinx.datetime) + implementation(libs.navigation.bar) + + androidTestImplementation(project(":core:testing")) +} \ No newline at end of file diff --git a/core/ui/src/androidTest/java/com/niyaj/ui/ExampleInstrumentedTest.kt b/core/ui/src/androidTest/java/com/niyaj/ui/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..75f78512 --- /dev/null +++ b/core/ui/src/androidTest/java/com/niyaj/ui/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.ui + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.ui.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/ui/src/main/AndroidManifest.xml b/core/ui/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/ui/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/ui/src/main/java/com/niyaj/ui/components/CategoriesData.kt b/core/ui/src/main/java/com/niyaj/ui/components/CategoriesData.kt new file mode 100644 index 00000000..fc48f1a8 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/CategoriesData.kt @@ -0,0 +1,100 @@ +package com.niyaj.ui.components + +import androidx.compose.foundation.layout.Arrangement +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.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Category +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +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.graphics.Color +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.model.Category + + +const val CATEGORY_ITEM_TAG = "Category-" + + +@Composable +fun CategoriesData( + categories: List, + selectedCategory: Int, + onSelect: (Int) -> Unit, +) { + LazyRow( + modifier = Modifier.fillMaxWidth() + ) { + items(categories) { category -> + CategoryData( + item = category, + doesSelected = { + selectedCategory == it + } , + onClick = onSelect, + ) + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun CategoryData( + modifier: Modifier = Modifier, + item: Category, + doesSelected: (Int) -> Boolean, + onClick: (Int) -> Unit, + selectedColor: Color = MaterialTheme.colorScheme.secondaryContainer, + unselectedColor: Color = MaterialTheme.colorScheme.surface +) { + val color = if (doesSelected(item.categoryId)) selectedColor else unselectedColor + + ElevatedCard( + modifier = modifier + .testTag(CATEGORY_ITEM_TAG.plus(item.categoryId)) + .padding(SpaceSmall), + onClick = { + onClick(item.categoryId) + }, + colors = CardDefaults.elevatedCardColors( + containerColor = color + ) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceSmall), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + CircularBox( + icon = Icons.Default.Category, + doesSelected = doesSelected(item.categoryId), + size = 25.dp, + text = item.categoryName + ) + + Spacer(modifier = Modifier.width(SpaceSmallMax)) + + Text( + text = item.categoryName, + style = MaterialTheme.typography.labelLarge, + fontWeight = FontWeight.SemiBold, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/ChosenBox.kt b/core/ui/src/main/java/com/niyaj/ui/components/ChosenBox.kt similarity index 96% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/ChosenBox.kt rename to core/ui/src/main/java/com/niyaj/ui/components/ChosenBox.kt index c45bfefa..f9fd816a 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/ChosenBox.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/ChosenBox.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.FastOutSlowInEasing @@ -14,7 +14,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Icon +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -35,7 +35,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceMini import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlin.math.absoluteValue @@ -198,7 +198,10 @@ fun MultiSelector( Layout( modifier = modifier - .border(BorderStroke(0.5.dp, MaterialTheme.colorScheme.secondary), RoundedCornerShape(8.dp)) + .border( + BorderStroke(0.5.dp, MaterialTheme.colorScheme.secondary), + RoundedCornerShape(8.dp) + ) .background(MaterialTheme.colorScheme.surface), content = { val colors = state.textColors @@ -214,7 +217,7 @@ fun MultiSelector( horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { - if (icons.isNotEmpty()){ + if (icons.isNotEmpty()) { Icon( imageVector = icons[index], contentDescription = option, diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/CircularBox.kt b/core/ui/src/main/java/com/niyaj/ui/components/CircularBox.kt similarity index 94% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/CircularBox.kt rename to core/ui/src/main/java/com/niyaj/ui/components/CircularBox.kt index 738ccee1..c6727340 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/CircularBox.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/CircularBox.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background @@ -9,9 +9,9 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.Icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -22,9 +22,9 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.IconSizeMedium -import com.niyaj.poposroom.features.common.ui.theme.IconSizeSmall -import com.niyaj.poposroom.features.common.utils.getAllCapitalizedLetters +import com.niyaj.designsystem.theme.IconSizeMedium +import com.niyaj.designsystem.theme.IconSizeSmall +import com.niyaj.common.utils.getAllCapitalizedLetters @Composable fun CircularBox( diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/ItemNotAvialable.kt b/core/ui/src/main/java/com/niyaj/ui/components/ItemNotAvialable.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/ItemNotAvialable.kt rename to core/ui/src/main/java/com/niyaj/ui/components/ItemNotAvialable.kt index 59eb687e..283a6422 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/ItemNotAvialable.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/ItemNotAvialable.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement @@ -6,30 +6,24 @@ 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.heightIn -import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CutCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Edit import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.ElevatedButton -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 androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.R -import com.niyaj.poposroom.features.common.ui.theme.ButtonSize -import com.niyaj.poposroom.features.common.ui.theme.SpaceMedium -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.light_outlineVariant +import com.niyaj.core.ui.R +import com.niyaj.designsystem.theme.SpaceMedium @Composable fun ItemNotAvailable( @@ -38,6 +32,9 @@ fun ItemNotAvailable( text: String = "", buttonText: String = "", showImage: Boolean = true, + icon: ImageVector = if (buttonText.contains("CREATE", true) + || buttonText.contains("ADD", true) + ) Icons.Default.Add else Icons.Default.Edit, image: Painter = painterResource(id = R.drawable.emptystate), onClick: () -> Unit = {}, ) { @@ -60,24 +57,22 @@ fun ItemNotAvailable( fontWeight = FontWeight.Normal, style = MaterialTheme.typography.bodySmall, textAlign = TextAlign.Center, - color = light_outlineVariant + color = MaterialTheme.colorScheme.error ) - if(buttonText.isNotEmpty()){ + if (buttonText.isNotEmpty()) { Spacer(modifier = Modifier.height(SpaceMedium)) - ElevatedButton( - onClick = { onClick() }, - shape= CutCornerShape(4.dp), - modifier = btnModifier.heightIn(ButtonSize), + StandardElevatedButton( + modifier = btnModifier, + text = buttonText, + icon = icon, + onClick = onClick, colors = ButtonDefaults.elevatedButtonColors( containerColor = MaterialTheme.colorScheme.secondary, contentColor = MaterialTheme.colorScheme.onSecondary - ) - ) { - Icon(imageVector = Icons.Default.Add, contentDescription = stringResource(id = R.string.add_icon) ) - Spacer(modifier = Modifier.width(SpaceSmall)) - Text(text = buttonText.uppercase()) - } + ), + shape = CutCornerShape(4.dp), + ) } } } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/LoadingIndicator.kt b/core/ui/src/main/java/com/niyaj/ui/components/LoadingIndicator.kt similarity index 83% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/LoadingIndicator.kt rename to core/ui/src/main/java/com/niyaj/ui/components/LoadingIndicator.kt index 1dd5e5bb..90be5c28 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/LoadingIndicator.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/LoadingIndicator.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -8,7 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag -import com.niyaj.poposroom.features.common.utils.Constants.LOADING_INDICATION +import com.niyaj.common.utils.Constants.LOADING_INDICATION @Composable fun LoadingIndicator() { diff --git a/core/ui/src/main/java/com/niyaj/ui/components/ModifierExtensions.kt b/core/ui/src/main/java/com/niyaj/ui/components/ModifierExtensions.kt new file mode 100644 index 00000000..c9dc3753 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/ModifierExtensions.kt @@ -0,0 +1,149 @@ +package com.niyaj.ui.components + +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.animateFloat +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.rememberInfiniteTransition +import androidx.compose.animation.core.tween +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.draw.drawWithContent +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.drawOutline +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.graphics.drawscope.rotate +import androidx.compose.ui.graphics.nativeCanvas +import androidx.compose.ui.unit.Dp + +val gradientColors = listOf( + Color.Red, + Color.Magenta, + Color.Blue, + Color.Cyan, + Color.Green, + Color.Yellow, + Color.Red +) + +fun Modifier.drawRainbowBorder( + strokeWidth: Dp, + durationMillis: Int, +) = composed { + + val infiniteTransition = rememberInfiniteTransition(label = "rotation") + val angle by infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable( + animation = tween(durationMillis, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), label = "rotation" + ) + + val brush = Brush.sweepGradient(gradientColors) + + Modifier.drawWithContent { + + val strokeWidthPx = strokeWidth.toPx() + val width = size.width + val height = size.height + + drawContent() + + with(drawContext.canvas.nativeCanvas) { + val checkPoint = saveLayer(null, null) + + // Destination + drawRect( + color = Color.Gray, + topLeft = Offset(strokeWidthPx / 2, strokeWidthPx / 2), + size = Size(width - strokeWidthPx, height - strokeWidthPx), + style = Stroke(strokeWidthPx) + ) + + // Source + rotate(angle) { + drawCircle( + brush = brush, + radius = size.width, + blendMode = BlendMode.SrcIn, + ) + } + + restoreToCount(checkPoint) + } + } +} + +fun Modifier.drawAnimatedBorder( + strokeWidth: Dp, + shape: Shape, + brush: (Size) -> Brush = { + Brush.sweepGradient(gradientColors) + }, + durationMillis: Int, +) = composed { + + val infiniteTransition = rememberInfiniteTransition(label = "rotation") + val angle by infiniteTransition.animateFloat( + initialValue = 0f, + targetValue = 360f, + animationSpec = infiniteRepeatable( + animation = tween(durationMillis, easing = LinearEasing), + repeatMode = RepeatMode.Restart + ), label = "rotation" + ) + + Modifier + .clip(shape) + .drawWithCache { + val strokeWidthPx = strokeWidth.toPx() + + val outline: Outline = shape.createOutline(size, layoutDirection, this) + + val pathBounds = outline.bounds + + onDrawWithContent { + // This is actual content of the Composable that this modifier is assigned to + drawContent() + + with(drawContext.canvas.nativeCanvas) { + val checkPoint = saveLayer(null, null) + + // Destination + + // We draw 2 times of the stroke with since we want actual size to be inside + // bounds while the outer stroke with is clipped with Modifier.clip + + // 🔥 Using a maskPath with op(this, outline.path, PathOperation.Difference) + // And GenericShape can be used as Modifier.border does instead of clip + drawOutline( + outline = outline, + color = Color.Gray, + style = Stroke(strokeWidthPx * 2) + ) + + // Source + rotate(angle) { + + drawCircle( + brush = brush(size), + radius = size.width, + blendMode = BlendMode.SrcIn, + ) + } + restoreToCount(checkPoint) + } + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/niyaj/ui/components/PhoneNoCountBox.kt b/core/ui/src/main/java/com/niyaj/ui/components/PhoneNoCountBox.kt new file mode 100644 index 00000000..4fe8d99d --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/PhoneNoCountBox.kt @@ -0,0 +1,67 @@ +package com.niyaj.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +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.graphics.Color +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.unit.dp +import com.niyaj.designsystem.theme.Olive + +@Composable +fun PhoneNoCountBox( + modifier : Modifier = Modifier, + count: Int, + totalCount: Int = 10, + backgroundColor: Color = Color.Transparent, + color: Color = Color.Gray, + errorColor: Color = Olive, +) { + val countColor = if (count <= 10) color else errorColor + val textColor = if (count >= 10) color else errorColor + + AnimatedVisibility( + visible = count != 0, + enter = fadeIn(), + exit = fadeOut(), + label = "Phone No Count Box", + ) { + Card( + modifier = modifier.background(backgroundColor), + shape = RoundedCornerShape(2.dp), + ) { + Row( + modifier = Modifier.padding(2.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly + ) { + Text( + text = count.toString(), + style = MaterialTheme.typography.labelSmall, + color = countColor, + ) + Text( + text = "/", + fontFamily = FontFamily.Cursive, + color = color, + ) + Text( + text = totalCount.toString(), + style = MaterialTheme.typography.labelSmall, + color = textColor, + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/ScaffoldNavActions.kt b/core/ui/src/main/java/com/niyaj/ui/components/ScaffoldNavActions.kt similarity index 90% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/ScaffoldNavActions.kt rename to core/ui/src/main/java/com/niyaj/ui/components/ScaffoldNavActions.kt index 0d828aca..3326e2c3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/ScaffoldNavActions.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/ScaffoldNavActions.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.animation.AnimatedContent import androidx.compose.animation.core.MutableTransitionState @@ -19,8 +19,8 @@ import androidx.compose.material3.IconButton import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag -import com.niyaj.poposroom.features.common.utils.Constants -import com.niyaj.poposroom.features.common.utils.Constants.SEARCH_ITEM_PLACEHOLDER +import com.niyaj.common.utils.Constants +import com.niyaj.common.utils.Constants.SEARCH_ITEM_PLACEHOLDER /** @@ -43,18 +43,18 @@ import com.niyaj.poposroom.features.common.utils.Constants.SEARCH_ITEM_PLACEHOLD @Composable fun ScaffoldNavActions( selectionCount: Int, - showSearchIcon : Boolean, - searchText : String, + showSearchIcon: Boolean, + searchText: String, placeholderText: String = SEARCH_ITEM_PLACEHOLDER, - showSearchBar : Boolean = false, - showSettingsIcon : Boolean = false, - onEditClick : () -> Unit, - onDeleteClick : () -> Unit, - onSelectAllClick : () -> Unit, - onClearClick : () -> Unit, - onSearchClick : () -> Unit, - onSettingsClick : () -> Unit = {}, - onSearchTextChanged : (String) -> Unit, + showSearchBar: Boolean = false, + showSettingsIcon: Boolean = false, + onEditClick: () -> Unit, + onDeleteClick: () -> Unit, + onSelectAllClick: () -> Unit, + onClearClick: () -> Unit, + onSearchClick: () -> Unit, + onSettingsClick: () -> Unit = {}, + onSearchTextChanged: (String) -> Unit, content: @Composable () -> Unit = {}, preActionContent: @Composable () -> Unit = {}, postActionContent: @Composable () -> Unit = {}, @@ -152,19 +152,19 @@ fun ScaffoldNavActions( @Composable fun ScaffoldNavActions( selectionCount: Int, - showSearchIcon : Boolean, - searchText : String, + showSearchIcon: Boolean, + searchText: String, showBottomBarActions: Boolean = false, placeholderText: String = SEARCH_ITEM_PLACEHOLDER, - showSearchBar : Boolean = false, - showSettings : Boolean = false, - onEditClick : () -> Unit, - onDeleteClick : () -> Unit, - onSelectAllClick : () -> Unit, - onClearClick : () -> Unit, - onSearchClick : () -> Unit, - onSettingsClick : () -> Unit = {}, - onSearchTextChanged : (String) -> Unit, + showSearchBar: Boolean = false, + showSettings: Boolean = false, + onEditClick: () -> Unit, + onDeleteClick: () -> Unit, + onSelectAllClick: () -> Unit, + onClearClick: () -> Unit, + onSearchClick: () -> Unit, + onSettingsClick: () -> Unit = {}, + onSearchTextChanged: (String) -> Unit, content: @Composable () -> Unit = {}, preActionContent: @Composable () -> Unit = {}, postActionContent: @Composable () -> Unit = {}, @@ -178,7 +178,7 @@ fun ScaffoldNavActions( onClearClick = onClearClick, onSearchTextChanged = onSearchTextChanged ) - }else { + } else { AnimatedContent( targetState = selectedState, transitionSpec = { diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/ScrollToTop.kt b/core/ui/src/main/java/com/niyaj/ui/components/ScrollToTop.kt similarity index 90% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/ScrollToTop.kt rename to core/ui/src/main/java/com/niyaj/ui/components/ScrollToTop.kt index 3ed3ffed..9df8d559 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/ScrollToTop.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/ScrollToTop.kt @@ -1,9 +1,9 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components -import androidx.compose.material.Icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material3.FilledTonalIconButton +import androidx.compose.material3.Icon import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/SelectedOrderBox.kt b/core/ui/src/main/java/com/niyaj/ui/components/SelectedOrderBox.kt similarity index 90% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/SelectedOrderBox.kt rename to core/ui/src/main/java/com/niyaj/ui/components/SelectedOrderBox.kt index b433eb7d..455efea8 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/SelectedOrderBox.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/SelectedOrderBox.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement @@ -26,9 +26,9 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.IconSizeLarge -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall +import com.niyaj.designsystem.theme.IconSizeLarge +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall @Composable fun SelectedOrderBox( diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardAssistChip.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardAssistChip.kt similarity index 88% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/StandardAssistChip.kt rename to core/ui/src/main/java/com/niyaj/ui/components/StandardAssistChip.kt index a5940c9b..66f77e64 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardAssistChip.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardAssistChip.kt @@ -1,20 +1,21 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.layout.size -import androidx.compose.material.Icon import androidx.compose.material3.AssistChip import androidx.compose.material3.AssistChipDefaults import androidx.compose.material3.ElevatedAssistChip import androidx.compose.material3.ElevatedFilterChip import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilterChipDefaults +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import com.niyaj.poposroom.features.common.ui.theme.IconSizeMini +import com.niyaj.designsystem.theme.IconSizeMini +import com.niyaj.designsystem.theme.LightColor8 @Composable fun StandardAssistChip( @@ -54,6 +55,7 @@ fun StandardFilterChip( icon: ImageVector? = null, selected: Boolean = false, containerColor: Color = MaterialTheme.colorScheme.surface, + selectedColor: Color = MaterialTheme.colorScheme.secondary, onClick: () -> Unit = {}, ) { ElevatedFilterChip( @@ -76,7 +78,9 @@ fun StandardFilterChip( } }, colors = FilterChipDefaults.elevatedFilterChipColors( - containerColor = containerColor + containerColor = containerColor, + selectedContainerColor = selectedColor, + selectedLabelColor = MaterialTheme.colorScheme.onSecondary ) ) } diff --git a/core/ui/src/main/java/com/niyaj/ui/components/StandardBottomNavigation.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardBottomNavigation.kt new file mode 100644 index 00000000..fd0580b3 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardBottomNavigation.kt @@ -0,0 +1,264 @@ +package com.niyaj.ui.components + +import android.view.animation.OvershootInterpolator +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.LinearOutSlowInEasing +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Assessment +import androidx.compose.material.icons.outlined.Home +import androidx.compose.material.icons.outlined.Inventory2 +import androidx.compose.material.icons.outlined.ShoppingCart +import androidx.compose.material.icons.rounded.Assessment +import androidx.compose.material.icons.rounded.Home +import androidx.compose.material.icons.rounded.Inventory2 +import androidx.compose.material.icons.rounded.ShoppingCart +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.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.exyte.animatednavbar.AnimatedNavigationBar +import com.exyte.animatednavbar.animation.balltrajectory.Teleport +import com.exyte.animatednavbar.animation.indendshape.Height +import com.exyte.animatednavbar.animation.indendshape.shapeCornerRadius +import com.exyte.animatednavbar.items.dropletbutton.DropletButton +import com.niyaj.core.ui.R +import com.niyaj.designsystem.theme.LightColor8 +import com.niyaj.designsystem.theme.Purple +import com.niyaj.ui.utils.Screens + + +@Stable +internal data class NavigationItem( + val index: Int, + val name: String, + val selected: Boolean, + val selectedIcon: Int, + val unselectedIcon: Int, + val onClick: () -> Unit, +) + +@Composable +private fun BottomNavigationBar( + navController: NavController, +) { + val currentRoute = navController.currentBackStackEntry?.destination?.route + + NavigationBar { + NavigationBarItem( + selected = currentRoute == Screens.HomeScreen, + label = { + val fontWeight = if (currentRoute == Screens.HomeScreen) + FontWeight.SemiBold else FontWeight.Normal + + Text( + text = "Home", + style = MaterialTheme.typography.labelSmall, + fontWeight = fontWeight, + ) + }, + onClick = { + navController.navigate(Screens.HomeScreen) + }, + icon = { + val icon = if (currentRoute == Screens.HomeScreen) { + Icons.Rounded.Home + } else Icons.Outlined.Home + + Icon(imageVector = icon, contentDescription = "Home") + } + ) + + NavigationBarItem( + selected = currentRoute == Screens.CartScreen, + label = { + val fontWeight = if (currentRoute == Screens.CartScreen) + FontWeight.SemiBold else FontWeight.Normal + + Text( + text = "Cart", + style = MaterialTheme.typography.labelSmall, + fontWeight = fontWeight, + ) + }, + onClick = { + navController.navigate(Screens.CartScreen) + }, + icon = { + val icon = if (currentRoute == Screens.CartScreen) { + Icons.Rounded.ShoppingCart + } else Icons.Outlined.ShoppingCart + + Icon(imageVector = icon, contentDescription = "Cart") + } + ) + + NavigationBarItem( + selected = currentRoute == Screens.OrderScreen, + label = { + val fontWeight = if (currentRoute == Screens.OrderScreen) + FontWeight.SemiBold else FontWeight.Normal + + Text( + text = "Orders", + style = MaterialTheme.typography.labelSmall, + fontWeight = fontWeight, + ) + }, + onClick = { + navController.navigate(Screens.OrderScreen) + }, + icon = { + val icon = if (currentRoute == Screens.OrderScreen) { + Icons.Rounded.Inventory2 + } else Icons.Outlined.Inventory2 + + Icon(imageVector = icon, contentDescription = "Orders") + } + ) + + NavigationBarItem( + selected = currentRoute == Screens.ReportScreen, + label = { + val fontWeight = if (currentRoute == Screens.ReportScreen) + FontWeight.SemiBold else FontWeight.Normal + + Text( + text = "Reports", + style = MaterialTheme.typography.labelSmall, + fontWeight = fontWeight, + ) + }, + onClick = { + navController.navigate(Screens.ReportScreen) + }, + icon = { + val icon = if (currentRoute == Screens.OrderScreen) { + Icons.Rounded.Assessment + } else Icons.Outlined.Assessment + + Icon(imageVector = icon, contentDescription = "Reports") + } + ) + } +} + + +@Composable +internal fun AnimatedBottomNavigationBar( + navController: NavController, + windowInsets: WindowInsets = NavigationBarDefaults.windowInsets, +) { + val currentRoute = navController.currentBackStackEntry?.destination?.route.hashCode() + + val navItems = listOf( + NavigationItem( + index = Screens.HomeScreen.hashCode(), + name = "Home", + selected = currentRoute == Screens.HomeScreen.hashCode(), + selectedIcon = R.drawable.round_home, + unselectedIcon = R.drawable.outline_home, + onClick = { + navController.navigate(Screens.HomeScreen) + } + ), + NavigationItem( + index = Screens.CartScreen.hashCode(), + name = "Cart", + selected = currentRoute == Screens.CartScreen.hashCode(), + selectedIcon = R.drawable.round_cart, + unselectedIcon = R.drawable.outline_cart, + onClick = { + navController.navigate(Screens.CartScreen) + } + ), + NavigationItem( + index = Screens.OrderScreen.hashCode(), + name = "Orders", + selected = currentRoute == Screens.OrderScreen.hashCode(), + selectedIcon = R.drawable.round_orders, + unselectedIcon = R.drawable.outline_orders, + onClick = { + navController.navigate(Screens.OrderScreen) + } + ), + NavigationItem( + index = Screens.ReportScreen.hashCode(), + name = "Reports", + selected = currentRoute == Screens.ReportScreen.hashCode(), + selectedIcon = R.drawable.round_reports, + unselectedIcon = R.drawable.outline_reports, + onClick = { + navController.navigate(Screens.ReportScreen) + } + ) + ) + + val index = navItems.indexOf(navItems.find { it.index == currentRoute }) + + AnimatedNavigationBar( + modifier = Modifier + .windowInsetsPadding(windowInsets) + .height(80.dp), + selectedIndex = index, + cornerRadius = shapeCornerRadius(0.dp), + barColor = LightColor8, + ballColor = MaterialTheme.colorScheme.secondary, + ballAnimation = Teleport(tween(Duration, easing = LinearOutSlowInEasing)), + indentAnimation = Height( + indentWidth = 56.dp, + indentHeight = 15.dp, + animationSpec = tween( + DoubleDuration, + easing = { OvershootInterpolator().getInterpolation(it) } + ) + ) + ) { + navItems.forEach { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + DropletButton( + modifier = Modifier.fillMaxWidth(), + isSelected = it.selected, + onClick = it.onClick, + icon = if (it.selected) it.selectedIcon else it.unselectedIcon, + dropletColor = Purple, + iconColor = MaterialTheme.colorScheme.tertiary, + size = 24.dp, + animationSpec = tween(durationMillis = Duration, easing = LinearEasing) + ) + + Spacer(modifier = Modifier.height(3.dp)) + + Text( + text = it.name, + color = if (it.selected) Purple else MaterialTheme.colorScheme.tertiary, + style = MaterialTheme.typography.labelSmall, + fontWeight = if (it.selected) FontWeight.SemiBold else FontWeight.Normal, + ) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardButton.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardButton.kt similarity index 71% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/StandardButton.kt rename to core/ui/src/main/java/com/niyaj/ui/components/StandardButton.kt index 65caef18..445bde97 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardButton.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardButton.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Spacer @@ -9,6 +9,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.ElevatedButton import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedButton @@ -19,8 +20,8 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.ButtonSize -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini +import com.niyaj.designsystem.theme.ButtonSize +import com.niyaj.designsystem.theme.SpaceMini @Composable fun StandardButton( @@ -58,6 +59,41 @@ fun StandardButton( } } +@Composable +fun StandardElevatedButton( + modifier: Modifier = Modifier, + iconModifier : Modifier = Modifier, + text: String, + icon: ImageVector? = null, + enabled: Boolean = true, + shape: Shape = RoundedCornerShape(SpaceMini), + colors: ButtonColors = ButtonDefaults.elevatedButtonColors(), + onClick: () -> Unit, +) { + ElevatedButton( + onClick = onClick, + enabled = enabled, + shape = shape, + colors = colors, + modifier = modifier + .testTag(text) + .heightIn(ButtonSize), + ) { + icon?.let { + Icon( + imageVector = icon, + contentDescription = text.plus("button"), + modifier = iconModifier + ) + Spacer(modifier = Modifier.width(SpaceMini)) + } + Text( + text = text.uppercase(), + style = MaterialTheme.typography.labelLarge, + ) + } +} + @Composable fun StandardOutlinedButton( diff --git a/core/ui/src/main/java/com/niyaj/ui/components/StandardCheckboxWithText.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardCheckboxWithText.kt new file mode 100644 index 00000000..b2913455 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardCheckboxWithText.kt @@ -0,0 +1,33 @@ +package com.niyaj.ui.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.Checkbox +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 + +@Composable +fun StandardCheckboxWithText( + modifier: Modifier = Modifier, + text: String, + checked: Boolean, + onCheckedChange: () -> Unit, +) { + Row( + modifier = modifier + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + Checkbox(checked = checked, onCheckedChange = { onCheckedChange() }) + + Text( + text = text, + style = MaterialTheme.typography.labelMedium + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardChip.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardChip.kt similarity index 94% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/StandardChip.kt rename to core/ui/src/main/java/com/niyaj/ui/components/StandardChip.kt index b2ba45de..f5074ed6 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardChip.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardChip.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.border @@ -24,12 +24,12 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.IconSizeSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.Constants.NOT_PAID -import com.niyaj.poposroom.features.common.utils.Constants.PAID -import com.niyaj.poposroom.features.utils.PaymentStatus +import com.niyaj.designsystem.theme.IconSizeSmall +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.common.utils.Constants.NOT_PAID +import com.niyaj.common.utils.Constants.PAID +import com.niyaj.ui.utils.PaymentStatus @Composable fun StandardOutlinedChip( diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardDrawer.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardDrawer.kt similarity index 66% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/StandardDrawer.kt rename to core/ui/src/main/java/com/niyaj/ui/components/StandardDrawer.kt index 1e58ba05..6abdda2b 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardDrawer.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardDrawer.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.Image import androidx.compose.foundation.background @@ -14,9 +14,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.AllInbox import androidx.compose.material.icons.filled.Assessment -import androidx.compose.material.icons.filled.Badge import androidx.compose.material.icons.filled.Bolt import androidx.compose.material.icons.filled.BreakfastDining import androidx.compose.material.icons.filled.Business @@ -27,7 +25,6 @@ import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.InsertLink import androidx.compose.material.icons.filled.Inventory import androidx.compose.material.icons.filled.KeyboardArrowDown -import androidx.compose.material.icons.filled.Logout import androidx.compose.material.icons.filled.Money import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.PeopleAlt @@ -35,8 +32,28 @@ import androidx.compose.material.icons.filled.Print import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.StickyNote2 import androidx.compose.material.icons.filled.SwitchAccount -import androidx.compose.material.icons.filled.Widgets import androidx.compose.material.icons.outlined.AccountCircle +import androidx.compose.material.icons.outlined.AllInbox +import androidx.compose.material.icons.outlined.Assessment +import androidx.compose.material.icons.outlined.Badge +import androidx.compose.material.icons.outlined.Bolt +import androidx.compose.material.icons.outlined.BreakfastDining +import androidx.compose.material.icons.outlined.Business +import androidx.compose.material.icons.outlined.Category +import androidx.compose.material.icons.outlined.Dns +import androidx.compose.material.icons.outlined.EventBusy +import androidx.compose.material.icons.outlined.Home +import androidx.compose.material.icons.outlined.InsertLink +import androidx.compose.material.icons.outlined.Inventory2 +import androidx.compose.material.icons.outlined.Logout +import androidx.compose.material.icons.outlined.Money +import androidx.compose.material.icons.outlined.Notifications +import androidx.compose.material.icons.outlined.PeopleAlt +import androidx.compose.material.icons.outlined.Print +import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material.icons.outlined.StickyNote2 +import androidx.compose.material.icons.outlined.SwitchAccount +import androidx.compose.material.icons.outlined.Widgets import androidx.compose.material3.Divider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -50,6 +67,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource @@ -57,44 +75,35 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.navigation.NavController -import com.niyaj.poposroom.R -import com.niyaj.poposroom.features.common.ui.theme.IconSizeLarge -import com.niyaj.poposroom.features.common.ui.theme.IconSizeSmall -import com.niyaj.poposroom.features.common.ui.theme.LightColor12 -import com.niyaj.poposroom.features.common.ui.theme.ProfilePictureSizeMedium -import com.niyaj.poposroom.features.common.ui.theme.SpaceLarge -import com.niyaj.poposroom.features.common.ui.theme.SpaceMedium -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.destinations.AbsentScreenDestination -import com.niyaj.poposroom.features.destinations.AddOnItemScreenDestination -import com.niyaj.poposroom.features.destinations.AddressScreenDestination -import com.niyaj.poposroom.features.destinations.CartOrderScreenDestination -import com.niyaj.poposroom.features.destinations.CategoryScreenDestination -import com.niyaj.poposroom.features.destinations.ChargesScreenDestination -import com.niyaj.poposroom.features.destinations.CustomerScreenDestination -import com.niyaj.poposroom.features.destinations.EmployeeScreenDestination -import com.niyaj.poposroom.features.destinations.ExpensesScreenDestination -import com.niyaj.poposroom.features.destinations.MainFeedScreenDestination -import com.niyaj.poposroom.features.destinations.OrderScreenDestination -import com.niyaj.poposroom.features.destinations.PaymentScreenDestination -import com.niyaj.poposroom.features.destinations.ProductScreenDestination -import com.ramcosta.composedestinations.navigation.navigate +import androidx.navigation.compose.currentBackStackEntryAsState +import com.niyaj.core.ui.R +import com.niyaj.designsystem.theme.IconSizeLarge +import com.niyaj.designsystem.theme.IconSizeSmall +import com.niyaj.designsystem.theme.LightColor12 +import com.niyaj.designsystem.theme.ProfilePictureSizeSmall +import com.niyaj.designsystem.theme.SpaceLarge +import com.niyaj.designsystem.theme.SpaceMedium +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.utils.Screens @Composable fun StandardDrawer( navController: NavController, - onCloseClick: () -> Unit + modifier: Modifier = Modifier, ) { - val currentRoute = navController.currentBackStackEntry?.destination?.route + val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route val expanded = remember { mutableStateOf(false) } val settingsExpanded = remember { mutableStateOf(false) } val employeeExpanded = remember { mutableStateOf(false) } val customersExpanded = remember { mutableStateOf(false) } val ordersExpanded = remember { mutableStateOf(false) } - ModalDrawerSheet { + ModalDrawerSheet( + modifier = modifier, + ) { Column( modifier = Modifier.weight(0.3f) ) { @@ -103,25 +112,26 @@ fun StandardDrawer( DrawerHeader(navController) Spacer(modifier = Modifier.height(SpaceLarge)) - - Divider(thickness = 1.dp, modifier = Modifier.fillMaxWidth()) - - Spacer(modifier = Modifier.height(SpaceMini)) } LazyColumn( modifier = Modifier .weight(2.5f) ) { + item { + Divider(thickness = 1.dp, modifier = Modifier.fillMaxWidth()) + Spacer(modifier = Modifier.height(SpaceMini)) + } + item { Spacer(modifier = Modifier.height(SpaceMedium)) DrawerItem( text = "Home", - icon = Icons.Default.Home, - selected = currentRoute == MainFeedScreenDestination.route, + icon = if (currentRoute == Screens.HomeScreen) Icons.Default.Home else Icons.Outlined.Home, + selected = currentRoute == Screens.HomeScreen, onClick = { - navController.navigate(MainFeedScreenDestination()) + navController.navigate(Screens.HomeScreen) } ) } @@ -131,10 +141,10 @@ fun StandardDrawer( DrawerItem( text = "View Orders", - icon = Icons.Default.Inventory, - selected = currentRoute == OrderScreenDestination.route, + icon = if (currentRoute == Screens.OrderScreen) Icons.Default.Inventory else Icons.Outlined.Inventory2, + selected = currentRoute == Screens.OrderScreen, onClick = { - navController.navigate(OrderScreenDestination()) + navController.navigate(Screens.OrderScreen) } ) } @@ -144,10 +154,10 @@ fun StandardDrawer( DrawerItem( text = "View Reports", - icon = Icons.Default.Assessment, - selected = false, // currentRoute == OrderScreenDestination.route, + icon = if (currentRoute == Screens.ReportScreen) Icons.Default.Assessment else Icons.Outlined.Assessment, + selected = currentRoute == Screens.ReportScreen, onClick = { -// navController.navigate(OrderScreenDestination()) + navController.navigate(Screens.ReportScreen) } ) } @@ -157,10 +167,10 @@ fun StandardDrawer( DrawerItem( text = "View Expenses", - icon = Icons.Default.StickyNote2, - selected = currentRoute == ExpensesScreenDestination.route, + icon = if (currentRoute == Screens.ExpensesScreen) Icons.Default.StickyNote2 else Icons.Outlined.StickyNote2, + selected = currentRoute == Screens.ExpensesScreen, onClick = { - navController.navigate(ExpensesScreenDestination()) + navController.navigate(Screens.ExpensesScreen) } ) } @@ -176,7 +186,6 @@ fun StandardDrawer( Spacer(modifier = Modifier.height(SpaceSmall)) - StandardExpandable( modifier = Modifier .fillMaxWidth() @@ -191,7 +200,7 @@ fun StandardDrawer( }, leading = { Icon( - imageVector = Icons.Default.AllInbox, + imageVector = Icons.Outlined.AllInbox, contentDescription = "Cart Order Icon", tint = MaterialTheme.colorScheme.onSurface ) @@ -212,25 +221,26 @@ fun StandardDrawer( }, content = { Column( - modifier = Modifier.padding(SpaceSmall), + modifier = Modifier + .padding(SpaceSmall), ) { DrawerItem( text = "Orders", - icon = Icons.Default.Inventory, - selected = currentRoute == OrderScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.OrderScreen) + Icons.Default.Inventory else Icons.Outlined.Inventory2, + selected = currentRoute == Screens.OrderScreen, onClick = { - navController.navigate(OrderScreenDestination()) + navController.navigate(Screens.OrderScreen) } ) Spacer(modifier = Modifier.height(SpaceSmall)) DrawerItem( text = "Cart Orders", - icon = Icons.Default.BreakfastDining, - selected = currentRoute == CartOrderScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.CartOrderScreen) + Icons.Default.BreakfastDining else Icons.Outlined.BreakfastDining, + selected = currentRoute == Screens.CartOrderScreen, onClick = { - navController.navigate(CartOrderScreenDestination()) + navController.navigate(Screens.CartOrderScreen) } ) } @@ -241,12 +251,17 @@ fun StandardDrawer( item { Spacer(modifier = Modifier.height(SpaceSmall)) + val doesExpanded = currentRoute in listOf( + Screens.CustomerScreen, + Screens.AddressScreen + ) + StandardExpandable( modifier = Modifier .fillMaxWidth() .padding(start = IconSizeSmall), dividerModifier = Modifier.padding(end = IconSizeSmall), - expanded = customersExpanded.value, + expanded = customersExpanded.value || doesExpanded, onExpandChanged = { customersExpanded.value = it }, @@ -255,7 +270,7 @@ fun StandardDrawer( }, leading = { Icon( - imageVector = Icons.Default.Badge, + imageVector = Icons.Outlined.Badge, contentDescription = "Customers, Addresses Icon", tint = MaterialTheme.colorScheme.onSurface ) @@ -280,11 +295,11 @@ fun StandardDrawer( ) { DrawerItem( text = "Customers", - icon = Icons.Default.PeopleAlt, - selected = currentRoute == CustomerScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.CustomerScreen) + Icons.Default.PeopleAlt else Icons.Outlined.PeopleAlt, + selected = currentRoute == Screens.CustomerScreen, onClick = { - navController.navigate(CustomerScreenDestination()) + navController.navigate(Screens.CustomerScreen) } ) @@ -292,11 +307,11 @@ fun StandardDrawer( DrawerItem( text = "Addresses", - icon = Icons.Default.Business, - selected = currentRoute == AddressScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.AddressScreen) + Icons.Default.Business else Icons.Outlined.Business, + selected = currentRoute == Screens.AddressScreen, onClick = { - navController.navigate(AddressScreenDestination()) + navController.navigate(Screens.AddressScreen) } ) } @@ -307,12 +322,18 @@ fun StandardDrawer( item { Spacer(modifier = Modifier.height(SpaceSmall)) + val doesExpanded = currentRoute in listOf( + Screens.EmployeeScreen, + Screens.PaymentScreen, + Screens.AbsentScreen + ) + StandardExpandable( modifier = Modifier .fillMaxWidth() .padding(start = IconSizeSmall), dividerModifier = Modifier.padding(end = IconSizeSmall), - expanded = employeeExpanded.value, + expanded = employeeExpanded.value || doesExpanded, onExpandChanged = { employeeExpanded.value = it }, @@ -321,7 +342,7 @@ fun StandardDrawer( }, leading = { Icon( - imageVector = Icons.Default.PeopleAlt, + imageVector = Icons.Outlined.PeopleAlt, contentDescription = "Employee, Salary, Advance Icon", tint = MaterialTheme.colorScheme.onSurface ) @@ -346,11 +367,11 @@ fun StandardDrawer( ) { DrawerItem( text = "Employees", - icon = Icons.Default.SwitchAccount, - selected = currentRoute == EmployeeScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.EmployeeScreen) + Icons.Default.SwitchAccount else Icons.Outlined.SwitchAccount, + selected = currentRoute == Screens.EmployeeScreen, onClick = { - navController.navigate(EmployeeScreenDestination()) + navController.navigate(Screens.EmployeeScreen) } ) @@ -358,11 +379,11 @@ fun StandardDrawer( DrawerItem( text = "Employee Absent Report", - icon = Icons.Default.EventBusy, - selected = currentRoute == AbsentScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.AbsentScreen) + Icons.Default.EventBusy else Icons.Outlined.EventBusy, + selected = currentRoute == Screens.AbsentScreen, onClick = { - navController.navigate(AbsentScreenDestination()) + navController.navigate(Screens.AbsentScreen) } ) @@ -370,11 +391,11 @@ fun StandardDrawer( DrawerItem( text = "Employee Payments", - icon = Icons.Default.Money, - selected = currentRoute == PaymentScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.PaymentScreen) + Icons.Default.Money else Icons.Outlined.Money, + selected = currentRoute == Screens.PaymentScreen, onClick = { - navController.navigate(PaymentScreenDestination()) + navController.navigate(Screens.PaymentScreen) } ) } @@ -385,12 +406,19 @@ fun StandardDrawer( item { Spacer(modifier = Modifier.height(SpaceSmall)) + val doesExpanded = currentRoute in listOf( + Screens.CategoryScreen, + Screens.ProductsScreen, + Screens.AddOnItemScreen, + Screens.ChargesScreen + ) + StandardExpandable( modifier = Modifier .fillMaxWidth() .padding(start = IconSizeSmall), dividerModifier = Modifier.padding(end = IconSizeSmall), - expanded = expanded.value, + expanded = expanded.value || doesExpanded, onExpandChanged = { expanded.value = it }, @@ -399,7 +427,7 @@ fun StandardDrawer( }, leading = { Icon( - imageVector = Icons.Default.Widgets, + imageVector = Icons.Outlined.Widgets, contentDescription = "Products, Categories Icon", tint = MaterialTheme.colorScheme.onSurface ) @@ -426,11 +454,11 @@ fun StandardDrawer( DrawerItem( text = "Categories", - icon = Icons.Default.Category, - selected = currentRoute == CategoryScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.CategoryScreen) + Icons.Default.Category else Icons.Outlined.Category, + selected = currentRoute == Screens.CategoryScreen, onClick = { - navController.navigate(CategoryScreenDestination()) + navController.navigate(Screens.CategoryScreen) } ) @@ -438,11 +466,11 @@ fun StandardDrawer( DrawerItem( text = "Products", - icon = Icons.Default.Dns, - selected = currentRoute == ProductScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.ProductsScreen) + Icons.Default.Dns else Icons.Outlined.Dns, + selected = currentRoute == Screens.ProductsScreen, onClick = { - navController.navigate(ProductScreenDestination()) + navController.navigate(Screens.ProductsScreen) } ) @@ -450,11 +478,11 @@ fun StandardDrawer( DrawerItem( text = "AddOn Item", - icon = Icons.Default.InsertLink, - selected = currentRoute == AddOnItemScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.AddOnItemScreen) + Icons.Default.InsertLink else Icons.Outlined.InsertLink, + selected = currentRoute == Screens.AddOnItemScreen, onClick = { - navController.navigate(AddOnItemScreenDestination()) + navController.navigate(Screens.AddOnItemScreen) } ) @@ -462,11 +490,11 @@ fun StandardDrawer( DrawerItem( text = "Charges Item", - icon = Icons.Default.Bolt, - selected = currentRoute == ChargesScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.ChargesScreen) + Icons.Default.Bolt else Icons.Outlined.Bolt, + selected = currentRoute == Screens.ChargesScreen, onClick = { - navController.navigate(ChargesScreenDestination()) + navController.navigate(Screens.ChargesScreen) } ) @@ -492,7 +520,7 @@ fun StandardDrawer( }, leading = { Icon( - imageVector = Icons.Default.Settings, + imageVector = Icons.Outlined.Settings, contentDescription = "Settings Icon", tint = MaterialTheme.colorScheme.onSurface ) @@ -517,9 +545,9 @@ fun StandardDrawer( ) { DrawerItem( text = "Reminders", - icon = Icons.Default.Notifications, - selected = false, // currentRoute == ReminderScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.ReminderScreen) + Icons.Default.Notifications else Icons.Outlined.Notifications, + selected = currentRoute == Screens.ReminderScreen, onClick = { // navController.navigate(ReminderScreenDestination()) } @@ -527,9 +555,9 @@ fun StandardDrawer( Spacer(modifier = Modifier.height(SpaceSmall)) DrawerItem( text = "App Settings", - icon = Icons.Default.Settings, - selected = false, // currentRoute == SettingsScreenDestination.route, - iconColor = MaterialTheme.colorScheme.secondary, + icon = if (currentRoute == Screens.SettingsScreen) + Icons.Default.Settings else Icons.Outlined.Settings, + selected = currentRoute == Screens.SettingsScreen, onClick = { // navController.navigate(SettingsScreenDestination()) } @@ -537,12 +565,12 @@ fun StandardDrawer( Spacer(modifier = Modifier.height(SpaceSmall)) DrawerItem( - text = "Print Settings", - icon = Icons.Default.Print, - selected = false, - iconColor = MaterialTheme.colorScheme.secondary, + text = "Printer Information", + icon = if (currentRoute == Screens.PrinterInfoScreen) + Icons.Default.Print else Icons.Outlined.Print, + selected = currentRoute == Screens.PrinterInfoScreen, onClick = { -// navController.navigate(PrintSettingsScreenDestination()) + navController.navigate(Screens.PrinterInfoScreen) } ) } @@ -560,7 +588,7 @@ fun StandardDrawer( DrawerItem( text = "Logout", - icon = Icons.Default.Logout, + icon = Icons.Outlined.Logout, selected = false, onClick = { @@ -581,12 +609,15 @@ fun DrawerHeader( verticalAlignment = Alignment.CenterVertically ) { Row( + modifier = Modifier.padding(start = SpaceSmallMax), verticalAlignment = Alignment.CenterVertically, ) { Image( - painterResource(id = R.drawable.popos), - contentDescription = null, - modifier = Modifier.size(ProfilePictureSizeMedium) + painterResource(id = R.drawable.splash), + contentDescription = "Restaurant Logo", + modifier = Modifier + .size(ProfilePictureSizeSmall) + .clip(RoundedCornerShape(SpaceMini)) ) Spacer(modifier = Modifier.width(SpaceMedium)) @@ -597,7 +628,7 @@ fun DrawerHeader( ) { Text( text = stringResource(id = R.string.restaurant_name), - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleLarge, fontWeight = FontWeight.Bold, ) @@ -614,8 +645,9 @@ fun DrawerHeader( IconButton( onClick = { -// navController.navigate() + navController.navigate(Screens.ProfileScreen) }, + modifier = Modifier.padding(end = SpaceSmall) ) { Icon( imageVector = Icons.Outlined.AccountCircle, @@ -634,6 +666,7 @@ fun DrawerItem( selected: Boolean, onClick: () -> Unit, iconColor: Color = MaterialTheme.colorScheme.onSurface, +// selectedColor: Color = MaterialTheme.colorScheme.secondary, ) { NavigationDrawerItem( icon = { diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardElevatedCard.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardElevatedCard.kt similarity index 90% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/StandardElevatedCard.kt rename to core/ui/src/main/java/com/niyaj/ui/components/StandardElevatedCard.kt index 776ffb91..60a7ce08 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardElevatedCard.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardElevatedCard.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.ExperimentalFoundationApi @@ -14,8 +14,8 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall @OptIn(ExperimentalFoundationApi::class) @Composable diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardExpandable.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardExpandable.kt similarity index 93% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/StandardExpandable.kt rename to core/ui/src/main/java/com/niyaj/ui/components/StandardExpandable.kt index 776cf580..c09481b0 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardExpandable.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardExpandable.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.FastOutSlowInEasing @@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.ContentAlpha import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material3.Divider @@ -25,15 +24,15 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.State +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.rotate import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmall @Composable fun StandardExpandable( @@ -68,7 +67,9 @@ fun StandardExpandable( modifier = Modifier .fillMaxWidth() .clickable( - interactionSource = MutableInteractionSource(), + interactionSource = remember { + MutableInteractionSource() + }, indication = null, enabled = rowClickable, ) { @@ -103,7 +104,7 @@ fun StandardExpandable( } ?: run { IconButton( modifier = Modifier - .alpha(ContentAlpha.medium) +// .alpha(ContentAlpha.medium) .rotate(expandAnimation.value), onClick = { onExpandChanged(!expanded) diff --git a/core/ui/src/main/java/com/niyaj/ui/components/StandardFAB.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardFAB.kt new file mode 100644 index 00000000..4224c5e8 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardFAB.kt @@ -0,0 +1,71 @@ +package com.niyaj.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material3.ExtendedFloatingActionButton +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 androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import com.niyaj.common.utils.Constants +import com.niyaj.designsystem.theme.SpaceSmall + +@Composable +fun StandardFAB( + fabVisible: Boolean, + showScrollToTop: Boolean = false, + fabText: String = Constants.FAB_TEXT, + fabIcon: ImageVector = Icons.Filled.Add, + containerColor: Color = MaterialTheme.colorScheme.tertiaryContainer, + onFabClick: () -> Unit, + onClickScroll: () -> Unit, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + ) { + AnimatedVisibility( + visible = showScrollToTop, + enter = fadeIn(), + exit = fadeOut(), + ) { + ScrollToTop(onClick = onClickScroll, containerColor = containerColor) + } + + Spacer(modifier = Modifier.height(SpaceSmall)) + + AnimatedVisibility( + visible = fabVisible, + enter = fadeIn() + slideInVertically( + initialOffsetY = { fullHeight -> + fullHeight / 4 + } + ), + exit = fadeOut() + slideOutVertically( + targetOffsetY = { fullHeight -> + fullHeight / 4 + } + ), + label = "FloatingActionButton" + ) { + ExtendedFloatingActionButton( + containerColor = MaterialTheme.colorScheme.primary, + onClick = onFabClick, + expanded = !showScrollToTop, + icon = { Icon(fabIcon, fabText) }, + text = { Text(text = fabText.uppercase()) }, + ) + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/niyaj/ui/components/StandardScaffold.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardScaffold.kt new file mode 100644 index 00000000..c79df7cb --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardScaffold.kt @@ -0,0 +1,385 @@ +package com.niyaj.ui.components + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.FastOutLinearInEasing +import androidx.compose.animation.core.tween +import androidx.compose.animation.core.updateTransition +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.animation.togetherWith +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Apps +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.DrawerValue +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FabPosition +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LargeTopAppBar +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalNavigationDrawer +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberDrawerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.geometry.lerp +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.lerp +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.testTag +import androidx.navigation.NavController +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.niyaj.common.utils.Constants +import com.niyaj.common.utils.Constants.STANDARD_BACK_BUTTON +import com.niyaj.designsystem.theme.RoyalPurple +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun StandardScaffold( + modifier: Modifier = Modifier, + navController: NavController, + title: String, + floatingActionButton: @Composable () -> Unit, + navActions: @Composable RowScope.() -> Unit, + bottomBar: @Composable () -> Unit = {}, + fabPosition: FabPosition = FabPosition.Center, + selectionCount: Int, + showBottomBar: Boolean = false, + showBackButton: Boolean = false, + onDeselect: () -> Unit = {}, + onBackClick: () -> Unit = {}, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + content: @Composable (PaddingValues) -> Unit, +) { + val drawerState = rememberDrawerState(DrawerValue.Closed) + val scope = rememberCoroutineScope() + val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() + + // Remember a SystemUiController + val systemUiController = rememberSystemUiController() + + val colorTransitionFraction = scrollBehavior.state.collapsedFraction + + val color = rememberUpdatedState(newValue = containerColorForPrimary(colorTransitionFraction)) + val navColor = rememberUpdatedState(newValue = containerColor(colorTransitionFraction)) + val shape = rememberUpdatedState(newValue = containerShape(colorTransitionFraction)) + + val selectedState = updateTransition(targetState = selectionCount, label = "selection count") + + SideEffect { + systemUiController.setStatusBarColor( + color = color.value, + darkIcons = false, + ) + + systemUiController.setNavigationBarColor( + color = navColor.value + ) + } + + ModalNavigationDrawer( + drawerState = drawerState, + drawerContent = { + StandardDrawer( + navController = navController + ) + }, + gesturesEnabled = true + ) { + Scaffold( + topBar = { + LargeTopAppBar( + title = { + Text(text = title) + }, + navigationIcon = { + if (showBackButton) { + IconButton( + onClick = onBackClick, + modifier = Modifier.testTag(STANDARD_BACK_BUTTON) + ) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = null, + tint = MaterialTheme.colorScheme.scrim + ) + } + } else { + AnimatedContent( + targetState = selectedState, + transitionSpec = { + (fadeIn()).togetherWith( + fadeOut(animationSpec = tween(200)) + ) + }, + label = "navigationIcon", + contentKey = { + it + } + ) { state -> + if (state.currentState != 0) { + IconButton( + onClick = onDeselect + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = Constants.CLEAR_ICON + ) + } + } else { + IconButton( + onClick = { + scope.launch { + drawerState.open() + } + } + ) { + Icon( + imageVector = Icons.Default.Apps, + contentDescription = null + ) + } + } + } + } + }, + actions = navActions, + scrollBehavior = scrollBehavior, + colors = TopAppBarDefaults.largeTopAppBarColors( + containerColor = MaterialTheme.colorScheme.primary, + scrolledContainerColor = RoyalPurple, + navigationIconContentColor = MaterialTheme.colorScheme.onPrimary, + titleContentColor = MaterialTheme.colorScheme.onPrimary, + actionIconContentColor = MaterialTheme.colorScheme.onPrimary + ) + ) + }, + bottomBar = { + AnimatedVisibility( + visible = showBottomBar, + label = "BottomBar", + enter = fadeIn() + slideInVertically( + initialOffsetY = { fullHeight -> + fullHeight / 4 + } + ), + exit = fadeOut() + slideOutVertically( + targetOffsetY = { fullHeight -> + fullHeight / 4 + } + ) + ) { + bottomBar() + } + }, + containerColor = MaterialTheme.colorScheme.primary, + floatingActionButton = floatingActionButton, + floatingActionButtonPosition = fabPosition, + snackbarHost = { SnackbarHost(snackbarHostState) }, + modifier = modifier + .testTag(title) + .fillMaxSize(), + ) { padding -> + ElevatedCard( + modifier = Modifier + .fillMaxSize() + .padding(padding) + .nestedScroll(scrollBehavior.nestedScrollConnection), + shape = shape.value, + elevation = CardDefaults.cardElevation(), + colors = CardDefaults.elevatedCardColors( + containerColor = MaterialTheme.colorScheme.onPrimary + ) + ) { + content(padding) + } + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun StandardScaffoldNew( + navController: NavController, + modifier: Modifier = Modifier, + title: String, + showDrawer: Boolean = true, + showBackButton: Boolean = false, + showBottomBar: Boolean, + fabPosition: FabPosition = FabPosition.Center, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + onBackClick: () -> Unit = { navController.navigateUp() }, + navigationIcon: @Composable () -> Unit = {}, + navActions: @Composable RowScope.() -> Unit = {}, + floatingActionButton: @Composable () -> Unit = {}, + bottomBar: @Composable () -> Unit = { AnimatedBottomNavigationBar(navController) }, + content: @Composable (PaddingValues) -> Unit = {}, +) { + val drawerState = rememberDrawerState(DrawerValue.Closed) + val scope = rememberCoroutineScope() + val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() + + // Remember a SystemUiController + val systemUiController = rememberSystemUiController() + + val colorTransitionFraction = scrollBehavior.state.collapsedFraction + + val color = rememberUpdatedState(newValue = containerColor(colorTransitionFraction)) + val shape = rememberUpdatedState(newValue = containerShape(colorTransitionFraction)) + val navColor = MaterialTheme.colorScheme.surface + + SideEffect { + systemUiController.setStatusBarColor( + color = color.value, + darkIcons = true, + ) + + systemUiController.setNavigationBarColor( + color = navColor + ) + } + + ModalNavigationDrawer( + drawerState = drawerState, + drawerContent = { + StandardDrawer( + navController = navController + ) + }, + gesturesEnabled = true + ) { + Scaffold( + topBar = { + LargeTopAppBar( + title = { + Text(text = title) + }, + navigationIcon = { + if (showBackButton) { + IconButton( + onClick = onBackClick, + modifier = Modifier.testTag(STANDARD_BACK_BUTTON) + ) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = null, + tint = MaterialTheme.colorScheme.scrim + ) + } + } else if (showDrawer) { + IconButton( + onClick = { + scope.launch { + drawerState.open() + } + } + ) { + Icon( + imageVector = Icons.Default.Apps, + contentDescription = null + ) + } + } else navigationIcon() + }, + actions = { + navActions() + }, + scrollBehavior = scrollBehavior, + ) + }, + bottomBar = { + AnimatedVisibility( + visible = showBottomBar, + label = "BottomBar", + enter = fadeIn() + slideInVertically( + initialOffsetY = { fullHeight -> + fullHeight / 4 + } + ), + exit = fadeOut() + slideOutVertically( + targetOffsetY = { fullHeight -> + fullHeight / 4 + } + ) + ) { + bottomBar() + } + }, + floatingActionButton = floatingActionButton, + floatingActionButtonPosition = fabPosition, + snackbarHost = { SnackbarHost(snackbarHostState) }, + modifier = modifier + .testTag(title) + .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection), + ) { padding -> + ElevatedCard( + modifier = Modifier + .fillMaxSize() + .padding(padding), + shape = shape.value, + elevation = CardDefaults.cardElevation(), + colors = CardDefaults.elevatedCardColors( + containerColor = MaterialTheme.colorScheme.onPrimary + ) + ) { + content(padding) + } + } + } +} + + +@Composable +internal fun containerColorForPrimary(colorTransitionFraction: Float): Color { + return lerp( + MaterialTheme.colorScheme.primary, + RoyalPurple, + FastOutLinearInEasing.transform(colorTransitionFraction) + ) +} + +@Composable +internal fun containerColor(colorTransitionFraction: Float): Color { + return lerp( + MaterialTheme.colorScheme.background, + MaterialTheme.colorScheme.surfaceVariant, + FastOutLinearInEasing.transform(colorTransitionFraction) + ) +} + +@Composable +internal fun containerShape(colorTransitionFraction: Float): Shape { + val data = lerp( + CornerRadius(48f, 48f), + CornerRadius(0f, 0f), + FastOutLinearInEasing.transform(colorTransitionFraction) + ) + + return RoundedCornerShape(data.x, data.y) +} + +const val Duration = 500 +const val DoubleDuration = 1000 \ No newline at end of file diff --git a/core/ui/src/main/java/com/niyaj/ui/components/StandardScaffoldWithBottomNavigation.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardScaffoldWithBottomNavigation.kt new file mode 100644 index 00000000..731fae3c --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardScaffoldWithBottomNavigation.kt @@ -0,0 +1,229 @@ +package com.niyaj.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Apps +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.DrawerValue +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FabPosition +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalNavigationDrawer +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.material3.rememberDrawerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.niyaj.common.utils.Constants +import com.niyaj.designsystem.theme.SpaceMedium +import com.niyaj.ui.utils.Screens +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun StandardScaffoldWithBottomNavigation( + modifier: Modifier = Modifier, + navController: NavController, + title: String = "", + selectedId: String = "0", + showBottomBar: Boolean = false, + showFab: Boolean = false, + showSearchBar: Boolean = false, + showSearchIcon: Boolean = false, + searchText: String = "", + searchPlaceholderText: String = "", + openSearchBar: () -> Unit = {}, + closeSearchBar: () -> Unit = {}, + onSearchTextChanged: (String) -> Unit = {}, + onClearClick: () -> Unit = {}, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, + bottomBar: @Composable () -> Unit = { AnimatedBottomNavigationBar(navController = navController) }, + navActions: @Composable RowScope.() -> Unit = {}, + content: @Composable (PaddingValues) -> Unit, +) { + val drawerState = rememberDrawerState(DrawerValue.Closed) + val scope = rememberCoroutineScope() + val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() + + // Remember a SystemUiController + val systemUiController = rememberSystemUiController() + + val colorTransitionFraction = scrollBehavior.state.collapsedFraction + + val shape = rememberUpdatedState(newValue = containerShape(colorTransitionFraction)) + val statusColor = MaterialTheme.colorScheme.surface + val navColor = MaterialTheme.colorScheme.surfaceVariant + + SideEffect { + systemUiController.setStatusBarColor(color = statusColor, darkIcons = true) + + systemUiController.setNavigationBarColor(color = navColor) + } + + ModalNavigationDrawer( + drawerState = drawerState, + drawerContent = { + StandardDrawer( + navController = navController + ) + }, + gesturesEnabled = true + ) { + Scaffold( + topBar = { + CenterAlignedTopAppBar( + title = { + if (title.isNotEmpty()) { + Text(text = title) + } else if (selectedId != "0") { + SelectedOrderBox( + modifier = Modifier + .padding(horizontal = SpaceMedium), + text = selectedId, + height = 40.dp, + onClick = { + navController.navigate(Screens.SelectOrderScreen) + } + ) + } + }, + navigationIcon = { + if (showSearchBar) { + IconButton( + onClick = closeSearchBar, + modifier = Modifier.testTag(Constants.STANDARD_BACK_BUTTON) + ) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = null, + tint = MaterialTheme.colorScheme.scrim + ) + } + } else { + IconButton( + onClick = { + scope.launch { + drawerState.open() + } + } + ) { + Icon( + imageVector = Icons.Default.Apps, + contentDescription = null + ) + } + } + }, + actions = { + if (showSearchBar) { + StandardSearchBar( + searchText = searchText, + placeholderText = searchPlaceholderText, + onClearClick = onClearClick, + onSearchTextChanged = onSearchTextChanged + ) + } else if (showSearchIcon) { + IconButton( + onClick = openSearchBar + ) { + Icon( + imageVector = Icons.Default.Search, + contentDescription = Constants.SEARCH_ICON + ) + } + } else { + navActions() + } + }, + scrollBehavior = scrollBehavior, + colors = TopAppBarDefaults.centerAlignedTopAppBarColors( + containerColor = MaterialTheme.colorScheme.surface, + scrolledContainerColor = MaterialTheme.colorScheme.surface + ) + ) + }, + floatingActionButton = { + AnimatedVisibility( + visible = showFab + ) { + FloatingActionButton( + onClick = { + navController.navigate(Screens.AddEditCartOrderScreen) + }, + containerColor = MaterialTheme.colorScheme.secondary + ) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Create new order" + ) + } + } + }, + floatingActionButtonPosition = FabPosition.End, + bottomBar = { + AnimatedVisibility( + visible = showBottomBar, + label = "BottomBar", + enter = fadeIn() + slideInVertically( + initialOffsetY = { fullHeight -> + fullHeight / 4 + } + ), + exit = fadeOut() + slideOutVertically( + targetOffsetY = { fullHeight -> + fullHeight / 4 + } + ) + ) { + bottomBar() + } + }, + snackbarHost = { SnackbarHost(snackbarHostState) }, + modifier = modifier + .testTag(title) + .fillMaxSize() + .nestedScroll(scrollBehavior.nestedScrollConnection), + ) { padding -> + ElevatedCard( + modifier = Modifier + .fillMaxSize() + .padding(padding), + shape = shape.value, + elevation = CardDefaults.cardElevation(), + colors = CardDefaults.elevatedCardColors( + containerColor = MaterialTheme.colorScheme.onPrimary + ) + ) { + content(padding) + } + } + } +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/niyaj/ui/components/StandardScaffoldWithOutDrawer.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardScaffoldWithOutDrawer.kt new file mode 100644 index 00000000..c91dbf50 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardScaffoldWithOutDrawer.kt @@ -0,0 +1,125 @@ +package com.niyaj.ui.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.BottomAppBar +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LargeTopAppBar +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.rememberUpdatedState +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.unit.dp +import com.google.accompanist.systemuicontroller.rememberSystemUiController +import com.niyaj.common.utils.Constants +import com.niyaj.ui.components.containerColor +import com.niyaj.ui.components.containerShape + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun StandardScaffoldWithOutDrawer( + title: String, + onBackClick: () -> Unit, + showBottomBar: Boolean = false, + bottomBar: @Composable () -> Unit = {}, + content: @Composable () -> Unit, +) { + val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() + // Remember a SystemUiController + val systemUiController = rememberSystemUiController() + + val colorTransitionFraction = scrollBehavior.state.collapsedFraction + + val color = rememberUpdatedState(newValue = containerColor(colorTransitionFraction)) + val shape = rememberUpdatedState(newValue = containerShape(colorTransitionFraction)) + + SideEffect { + systemUiController.setStatusBarColor( + color = color.value, + darkIcons = true, + ) + + systemUiController.setNavigationBarColor( + color = color.value + ) + } + + Scaffold( + modifier = Modifier + .testTag(title) + .fillMaxWidth() + .nestedScroll(scrollBehavior.nestedScrollConnection), + topBar = { + LargeTopAppBar( + navigationIcon = { + IconButton( + onClick = onBackClick, + modifier = Modifier.testTag(Constants.STANDARD_BACK_BUTTON) + ) { + Icon( + imageVector = Icons.Default.ArrowBack, + contentDescription = null, + tint = MaterialTheme.colorScheme.scrim + ) + } + }, + title = { + Text(text = title) + }, + scrollBehavior = scrollBehavior, + ) + }, + bottomBar = { + AnimatedVisibility( + visible = showBottomBar, + enter = fadeIn() + slideInVertically( + initialOffsetY = { fullHeight -> + fullHeight / 4 + } + ), + exit = fadeOut() + slideOutVertically( + targetOffsetY = { fullHeight -> + fullHeight / 4 + } + ) + ) { + BottomAppBar { + bottomBar() + } + } + } + ) { padding -> + ElevatedCard( + modifier = Modifier + .fillMaxSize() + .padding(padding), + shape = shape.value, + colors = CardDefaults.elevatedCardColors( + containerColor = MaterialTheme.colorScheme.surface + ), + elevation = CardDefaults.elevatedCardElevation( + defaultElevation = colorTransitionFraction.dp + ) + ) { + content() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardSearchBar.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardSearchBar.kt similarity index 89% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/StandardSearchBar.kt rename to core/ui/src/main/java/com/niyaj/ui/components/StandardSearchBar.kt index d9d57a05..71ee166d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardSearchBar.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardSearchBar.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.LocalContentAlpha import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material3.Icon @@ -33,9 +32,9 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADDON_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.common.utils.Constants.SEARCH_BAR_CLEAR_BUTTON -import com.niyaj.poposroom.features.common.utils.Constants.STANDARD_SEARCH_BAR +import com.niyaj.common.utils.Constants.SEARCH_BAR_CLEAR_BUTTON +import com.niyaj.common.utils.Constants.SEARCH_PLACEHOLDER +import com.niyaj.common.utils.Constants.STANDARD_SEARCH_BAR @OptIn(ExperimentalComposeUiApi::class) @Composable @@ -70,14 +69,14 @@ fun StandardSearchBar( Text( text = placeholderText, color = Color.LightGray, - modifier = Modifier.testTag(ADDON_SEARCH_PLACEHOLDER) + modifier = Modifier.testTag(SEARCH_PLACEHOLDER) ) }, colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = Color.Transparent, unfocusedBorderColor = Color.Transparent, focusedContainerColor = Color.Transparent, - cursorColor = LocalContentColor.current.copy(alpha = LocalContentAlpha.current) + cursorColor = LocalContentColor.current.copy(alpha = 0.2f) ), trailingIcon = { if (searchText.isNotEmpty()) { diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardTextField.kt b/core/ui/src/main/java/com/niyaj/ui/components/StandardTextField.kt similarity index 83% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/StandardTextField.kt rename to core/ui/src/main/java/com/niyaj/ui/components/StandardTextField.kt index bb4a4f73..378dffae 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/StandardTextField.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/StandardTextField.kt @@ -1,12 +1,12 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.IconButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Visibility import androidx.compose.material.icons.filled.VisibilityOff 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 @@ -15,14 +15,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation import androidx.compose.ui.text.input.VisualTransformation -import com.niyaj.poposroom.R -import com.niyaj.poposroom.features.common.utils.Constants.TEXT_FIELD_LEADING_ICON -import com.niyaj.poposroom.features.common.utils.Constants.TEXT_FIELD_TRAILING_ICON +import com.niyaj.common.utils.Constants.PASSWORD_HIDDEN_ICON +import com.niyaj.common.utils.Constants.PASSWORD_SHOWN_ICON +import com.niyaj.common.utils.Constants.TEXT_FIELD_LEADING_ICON +import com.niyaj.common.utils.Constants.TEXT_FIELD_TRAILING_ICON @Composable fun StandardTextField( @@ -43,7 +43,9 @@ fun StandardTextField( isPasswordVisible: Boolean = false, errorTextTag: String = label.plus("Error"), onPasswordToggleClick: (Boolean) -> Unit = {}, - onTrailingIconClick: () -> Unit = {} + prefix: @Composable() (() -> Unit)? = null, + suffix: @Composable() (() -> Unit)? = null, + onTrailingIconClick: () -> Unit = {}, ) { TextField( value = value, @@ -63,7 +65,7 @@ fun StandardTextField( Icon(imageVector = leadingIcon, contentDescription = TEXT_FIELD_LEADING_ICON) }, trailingIcon = { - if(isPasswordToggleDisplayed) { + if (isPasswordToggleDisplayed) { IconButton( onClick = { onPasswordToggleClick(!isPasswordVisible) @@ -77,13 +79,13 @@ fun StandardTextField( Icons.Filled.Visibility }, contentDescription = if (isPasswordVisible) { - stringResource(id = R.string.password_hidden_content_description) + PASSWORD_HIDDEN_ICON } else { - stringResource(id = R.string.password_visible_content_description) + PASSWORD_SHOWN_ICON } ) } - }else { + } else { trailingIcon?.let { IconButton( onClick = onTrailingIconClick, @@ -93,10 +95,8 @@ fun StandardTextField( } } }, - prefix = { - - }, - suffix = {}, + prefix = prefix, + suffix = suffix, supportingText = { errorText?.let { Text( @@ -138,6 +138,7 @@ fun StandardOutlinedTextField( readOnly: Boolean = false, enabled: Boolean = true, errorTextTag: String = label.plus("Error"), + message: String? = null, isPasswordToggleDisplayed: Boolean = keyboardType == KeyboardType.Password, isPasswordVisible: Boolean = false, onPasswordToggleClick: (Boolean) -> Unit = {}, @@ -162,7 +163,7 @@ fun StandardOutlinedTextField( leadingIcon = { Icon(imageVector = leadingIcon, contentDescription = TEXT_FIELD_LEADING_ICON) }, - trailingIcon = if(isPasswordToggleDisplayed) { + trailingIcon = if (isPasswordToggleDisplayed) { val icon: @Composable () -> Unit = { IconButton( onClick = { @@ -170,17 +171,17 @@ fun StandardOutlinedTextField( }, modifier = Modifier ) { - androidx.compose.material.Icon( + Icon( imageVector = if (isPasswordVisible) { Icons.Filled.VisibilityOff } else { Icons.Filled.Visibility }, - tint = androidx.compose.material.MaterialTheme.colors.secondaryVariant, + tint = MaterialTheme.colorScheme.secondary, contentDescription = if (isPasswordVisible) { - stringResource(id = R.string.password_visible_content_description) + PASSWORD_HIDDEN_ICON } else { - stringResource(id = R.string.password_hidden_content_description) + PASSWORD_SHOWN_ICON } ) } @@ -190,10 +191,17 @@ fun StandardOutlinedTextField( prefix = prefix, suffix = suffix, supportingText = { - errorText?.let { + if (errorText != null) { Text( modifier = Modifier.testTag(errorTextTag), - text = it, + text = errorText, + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.error + ) + } else if (message != null) { + Text( + modifier = Modifier.testTag(errorTextTag), + text = message, style = MaterialTheme.typography.labelSmall, color = MaterialTheme.colorScheme.error ) diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/TextWithCount.kt b/core/ui/src/main/java/com/niyaj/ui/components/TextWithCount.kt similarity index 93% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/TextWithCount.kt rename to core/ui/src/main/java/com/niyaj/ui/components/TextWithCount.kt index 7fe07350..cf09b887 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/TextWithCount.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/TextWithCount.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -11,8 +11,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Text 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 @@ -22,8 +22,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall @Composable fun TextWithCount( @@ -86,7 +86,7 @@ fun TextWithCount( icon = leadingIcon, fontWeight = FontWeight.Bold ) - + Row { trailingText?.let { CountBox(count = it) @@ -104,7 +104,7 @@ fun CountBox( modifier: Modifier = Modifier, count: String, textColor: Color = MaterialTheme.colorScheme.onPrimary, - backgroundColor: Color = MaterialTheme.colorScheme.primary + backgroundColor: Color = MaterialTheme.colorScheme.primary ) { Column( modifier = modifier diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/TextWithIcon.kt b/core/ui/src/main/java/com/niyaj/ui/components/TextWithIcon.kt similarity index 77% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/TextWithIcon.kt rename to core/ui/src/main/java/com/niyaj/ui/components/TextWithIcon.kt index 68c87451..da62ed1d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/TextWithIcon.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/TextWithIcon.kt @@ -1,11 +1,17 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components +import androidx.compose.foundation.layout.Arrangement 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.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ErrorOutline +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -19,8 +25,9 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow -import com.niyaj.poposroom.features.common.ui.theme.SpaceMedium -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceMedium +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall @Composable fun TextWithIcon( @@ -204,4 +211,48 @@ fun NoteText( ) } } +} + +@Composable +fun NoteCard( + modifier: Modifier = Modifier, + iconModifier: Modifier = Modifier.size(SpaceMedium), + text: String, + icon: ImageVector = Icons.Default.ErrorOutline, + backgroundColor: Color = MaterialTheme.colorScheme.errorContainer, + textColor : Color = MaterialTheme.colorScheme.error, + fontWeight: FontWeight = FontWeight.Normal, +) { + Card( + modifier = modifier + .fillMaxWidth(), + shape = RoundedCornerShape(SpaceMini), + colors = CardDefaults.cardColors( + containerColor = backgroundColor + ) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceMini), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(SpaceSmall), + ) { + Icon( + imageVector = icon, + contentDescription = text, + tint = textColor, + modifier = iconModifier, + ) + + Text( + text = text, + style = MaterialTheme.typography.labelSmall, + fontWeight = fontWeight, + maxLines = 3, + overflow = TextOverflow.Ellipsis, + color = textColor, + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/TitleWithIcon.kt b/core/ui/src/main/java/com/niyaj/ui/components/TitleWithIcon.kt similarity index 97% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/TitleWithIcon.kt rename to core/ui/src/main/java/com/niyaj/ui/components/TitleWithIcon.kt index fa8e9465..e778af0e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/TitleWithIcon.kt +++ b/core/ui/src/main/java/com/niyaj/ui/components/TitleWithIcon.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.ui.components import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn diff --git a/core/ui/src/main/java/com/niyaj/ui/components/TwoGridTexts.kt b/core/ui/src/main/java/com/niyaj/ui/components/TwoGridTexts.kt new file mode 100644 index 00000000..db866678 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/components/TwoGridTexts.kt @@ -0,0 +1,75 @@ +package com.niyaj.ui.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +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.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign + +@Composable +fun TwoGridTexts( + modifier : Modifier = Modifier, + textOne: String = "", + textTwo: String = "", + isTitle: Boolean = false, +) { + + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ){ + Text( + modifier = Modifier.weight(2.5f, true), + text = textOne, + style = MaterialTheme.typography.labelSmall, + fontWeight = FontWeight.SemiBold, + ) + + Text( + modifier = Modifier.weight(0.5f, true), + text = textTwo, + style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.End, + fontWeight = if(isTitle) FontWeight.SemiBold else FontWeight.Normal, + ) + } +} + + +@Composable +fun TwoGridText( + modifier : Modifier = Modifier, + textOne: String = "", + textTwo: String = "", + textColor: Color = Color.Black +) { + Row( + modifier = modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ){ + Text( + modifier = Modifier, + text = textOne, + style = MaterialTheme.typography.labelMedium, + fontWeight = FontWeight.SemiBold, + color = textColor, + ) + + Text( + modifier = Modifier, + text = textTwo, + style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.End, + fontWeight = FontWeight.Normal, + color = textColor, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/event/BaseViewModel.kt b/core/ui/src/main/java/com/niyaj/ui/event/BaseViewModel.kt similarity index 96% rename from app/src/main/java/com/niyaj/poposroom/features/common/event/BaseViewModel.kt rename to core/ui/src/main/java/com/niyaj/ui/event/BaseViewModel.kt index 0474d482..dfd8baa0 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/event/BaseViewModel.kt +++ b/core/ui/src/main/java/com/niyaj/ui/event/BaseViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.event +package com.niyaj.ui.event import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateListOf @@ -6,7 +6,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.utils.UiEvent +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/event/ItemEvents.kt b/core/ui/src/main/java/com/niyaj/ui/event/ItemEvents.kt similarity index 88% rename from app/src/main/java/com/niyaj/poposroom/features/common/event/ItemEvents.kt rename to core/ui/src/main/java/com/niyaj/ui/event/ItemEvents.kt index fbe07738..0ae7fdd9 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/event/ItemEvents.kt +++ b/core/ui/src/main/java/com/niyaj/ui/event/ItemEvents.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.event +package com.niyaj.ui.event sealed interface ItemEvents { diff --git a/core/ui/src/main/java/com/niyaj/ui/event/UiState.kt b/core/ui/src/main/java/com/niyaj/ui/event/UiState.kt new file mode 100644 index 00000000..bb49f50b --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/event/UiState.kt @@ -0,0 +1,9 @@ +package com.niyaj.ui.event + +sealed interface UiState { + object Loading : UiState + + object Empty : UiState + + data class Success(val data: T) : UiState +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/utils/BottomSheetWithCloseIcon.kt b/core/ui/src/main/java/com/niyaj/ui/utils/BottomSheetWithCloseIcon.kt similarity index 91% rename from app/src/main/java/com/niyaj/poposroom/features/common/utils/BottomSheetWithCloseIcon.kt rename to core/ui/src/main/java/com/niyaj/ui/utils/BottomSheetWithCloseIcon.kt index 7161041f..1deae665 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/utils/BottomSheetWithCloseIcon.kt +++ b/core/ui/src/main/java/com/niyaj/ui/utils/BottomSheetWithCloseIcon.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.utils +package com.niyaj.ui.utils import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -22,9 +22,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.SpaceLarge -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax +import com.niyaj.designsystem.theme.SpaceLarge +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax @Composable fun BottomSheetWithCloseDialog( diff --git a/core/ui/src/main/java/com/niyaj/ui/utils/JankStatsExtensions.kt b/core/ui/src/main/java/com/niyaj/ui/utils/JankStatsExtensions.kt new file mode 100644 index 00000000..fce74a90 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/utils/JankStatsExtensions.kt @@ -0,0 +1,77 @@ +package com.niyaj.ui.utils + +import androidx.compose.foundation.gestures.ScrollableState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.DisposableEffectResult +import androidx.compose.runtime.DisposableEffectScope +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.platform.LocalView +import androidx.metrics.performance.PerformanceMetricsState +import androidx.metrics.performance.PerformanceMetricsState.Holder +import kotlinx.coroutines.CoroutineScope + +/** + * Retrieves [PerformanceMetricsState.Holder] from current [LocalView] and + * remembers it until the View changes. + * @see PerformanceMetricsState.getHolderForHierarchy + */ +@Composable +fun rememberMetricsStateHolder(): Holder { + val localView = LocalView.current + + return remember(localView) { + PerformanceMetricsState.getHolderForHierarchy(localView) + } +} + +/** + * Convenience function to work with [PerformanceMetricsState] state. The side effect is + * re-launched if any of the [keys] value is not equal to the previous composition. + * @see TrackDisposableJank if you need to work with DisposableEffect to cleanup added state. + */ +@Composable +fun TrackJank( + vararg keys: Any?, + reportMetric: suspend CoroutineScope.(state: Holder) -> Unit, +) { + val metrics = rememberMetricsStateHolder() + LaunchedEffect(metrics, *keys) { + reportMetric(metrics) + } +} + +/** + * Convenience function to work with [PerformanceMetricsState] state that needs to be cleaned up. + * The side effect is re-launched if any of the [keys] value is not equal to the previous composition. + */ +@Composable +fun TrackDisposableJank( + vararg keys: Any?, + reportMetric: DisposableEffectScope.(state: Holder) -> DisposableEffectResult, +) { + val metrics = rememberMetricsStateHolder() + DisposableEffect(metrics, *keys) { + reportMetric(this, metrics) + } +} + +/** + * Track jank while scrolling anything that's scrollable. + */ +@Composable +fun TrackScrollJank(scrollableState: ScrollableState, stateName: String) { + TrackJank(scrollableState) { metricsHolder -> + snapshotFlow { scrollableState.isScrollInProgress }.collect { isScrollInProgress -> + metricsHolder.state?.apply { + if (isScrollInProgress) { + putState(stateName, "Scrolling=true") + } else { + removeState(stateName) + } + } + } + } +} diff --git a/core/ui/src/main/java/com/niyaj/ui/utils/LazyListUtils.kt b/core/ui/src/main/java/com/niyaj/ui/utils/LazyListUtils.kt new file mode 100644 index 00000000..56518b28 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/utils/LazyListUtils.kt @@ -0,0 +1,34 @@ +package com.niyaj.ui.utils + +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.lazy.grid.LazyGridState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue + +val LazyListState.isScrolled: Boolean + get() = derivedStateOf { firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0 }.value + +@Composable +fun LazyListState.isScrollingUp(): Boolean { + var previousIndex by remember(this) { mutableIntStateOf(firstVisibleItemIndex) } + var previousScrollOffset by remember(this) { mutableIntStateOf(firstVisibleItemScrollOffset) } + return remember(this) { + derivedStateOf { + if (previousIndex != firstVisibleItemIndex) { + previousIndex > firstVisibleItemIndex + } else { + previousScrollOffset >= firstVisibleItemScrollOffset + }.also { + previousIndex = firstVisibleItemIndex + previousScrollOffset = firstVisibleItemScrollOffset + } + } + }.value +} + +val LazyGridState.isScrolled: Boolean + get() = derivedStateOf { firstVisibleItemIndex > 0 || firstVisibleItemScrollOffset > 0 }.value diff --git a/app/src/main/java/com/niyaj/poposroom/features/utils/PaymentStatus.kt b/core/ui/src/main/java/com/niyaj/ui/utils/PaymentStatus.kt similarity index 51% rename from app/src/main/java/com/niyaj/poposroom/features/utils/PaymentStatus.kt rename to core/ui/src/main/java/com/niyaj/ui/utils/PaymentStatus.kt index 08429256..0d8ea2c7 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/utils/PaymentStatus.kt +++ b/core/ui/src/main/java/com/niyaj/ui/utils/PaymentStatus.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.utils +package com.niyaj.ui.utils import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close @@ -7,7 +7,10 @@ import androidx.compose.material.icons.filled.HowToReg import androidx.compose.ui.graphics.vector.ImageVector sealed class PaymentStatus(val status: String, val icon: ImageVector, val order: Int) { - object NotPaid: PaymentStatus(status = "Not Paid", icon = Icons.Default.Close, order = 1) - object Absent: PaymentStatus(status = "Absent", icon = Icons.Default.EventBusy, order = 2) - object Paid: PaymentStatus(status = "Paid", icon = Icons.Default.HowToReg, order = 3) + + data object NotPaid : PaymentStatus(status = "Not Paid", icon = Icons.Default.Close, order = 1) + + data object Absent : PaymentStatus(status = "Absent", icon = Icons.Default.EventBusy, order = 2) + + data object Paid : PaymentStatus(status = "Paid", icon = Icons.Default.HowToReg, order = 3) } \ No newline at end of file diff --git a/core/ui/src/main/java/com/niyaj/ui/utils/Screens.kt b/core/ui/src/main/java/com/niyaj/ui/utils/Screens.kt new file mode 100644 index 00000000..bc03b108 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/utils/Screens.kt @@ -0,0 +1,82 @@ +package com.niyaj.ui.utils + +object Screens { + + const val AddOnItemScreen = "add_on_item_screen" + const val AddEditAddOnItemScreen = "add_edit_add_on_item_screen" + + const val SplashScreen= "splash_screen" + + const val LoginScreen = "login_screen" + + const val HomeScreen = "home_screen" + + const val ProfileScreen = "profile_screen" + + const val CartScreen = "cart_screen" + + const val OrderScreen = "order_screen" + + const val OrderDetailsScreen = "order_details_screen" + + const val NotificationScreen= "notification" + + const val SearchScreen = "search_screen" + + const val SettingsScreen = "settings_screen" + + const val PrintSettingsScreen = "print_settings_screen" + + const val CategoryScreen = "category_screen" + const val AddEditCategoryScreen = "add_edit_category_screen" + + const val ProductsScreen = "products_screen" + const val AddEditProductScreen = "add_edit_product_screen" + + const val AddressScreen = "address_screen" + + const val CustomerScreen = "customer_screen" + + const val CartOrderScreen = "cart_order_screen" + const val AddEditCartOrderScreen = "add_edit_cart_order_screen" + + const val SelectOrderScreen = "select_order_screen" + + const val CartOrderSettingsScreen = "cart_order_settings_screen" + + const val ChargesScreen = "charges_screen" + + const val PartnerScreen = "partner_screen" + + const val AddEditPartnerScreen = "add_edit_partner_screen" + + const val EmployeeScreen = "employee_screen" + const val AddEditEmployeeScreen = "add_edit_employee_screen" + + + const val PaymentScreen = "payment_screen" + const val AddEditPaymentScreen = "add_edit_payment_screen" + + const val AbsentScreen = "absent_screen" + const val AddEditAbsentScreen = "add_edit_absent_screen" + + const val ExpensesCategoryScreen = "expenses_category_screen" + + const val ExpensesSubScreen = "expenses_sub_category_screen" + + const val ExpensesScreen = "expenses_screen" + + const val AddExpensesScreen = "add_expenses_screen" + + const val EditExpensesScreen = "edit_expenses_screen" + + const val ReportScreen = "report_screen" + + const val ViewLastSevenDaysReports= "view_last_seven_days_reports" + + const val ReminderScreen = "reminder_screen" + + const val PrinterInfoScreen = "printer_info_screen" + const val UpdatePrinterInfoScreen = "update_printer_info_screen" + +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/niyaj/ui/utils/SheetLayout.kt b/core/ui/src/main/java/com/niyaj/ui/utils/SheetLayout.kt new file mode 100644 index 00000000..248bd39e --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/utils/SheetLayout.kt @@ -0,0 +1,68 @@ +package com.niyaj.ui.utils + +import androidx.compose.runtime.Composable + +@Composable +fun SheetLayout( + current: SheetScreen, + onCloseBottomSheet: () -> Unit, +) { + BottomSheetWithCloseDialog( + text = current.type, + onClosePressed = onCloseBottomSheet + ) { + when (current) { + is SheetScreen.CreateNewAddOnItem -> { +// AddEditItemScreen(closeSheet = onCloseBottomSheet) + } + + is SheetScreen.UpdateAddOnItem -> { +// AddEditItemScreen(addOnItemId = current.itemId, closeSheet = onCloseBottomSheet) + } + + is SheetScreen.CreateNewAddress -> { +// AddEditAddressScreen(closeSheet = onCloseBottomSheet) + } + + is SheetScreen.UpdateAddress -> { +// AddEditAddressScreen( +// addressId = current.addressId, +// closeSheet = onCloseBottomSheet +// ) + } + + is SheetScreen.CreateNewCharges -> { +// AddEditChargesScreen(closeSheet = onCloseBottomSheet) + } + + is SheetScreen.UpdateCharges -> { +// AddEditChargesScreen( +// chargesId = current.chargesId, +// closeSheet = onCloseBottomSheet +// ) + } + + is SheetScreen.CreateNewCategory -> { +// AddEditCategoryScreen(closeSheet = onCloseBottomSheet) + } + + is SheetScreen.UpdateCategory -> { +// AddEditCategoryScreen( +// categoryId = current.categoryId, +// closeSheet = onCloseBottomSheet +// ) + } + + is SheetScreen.CreateNewCustomer -> { +// AddEditCustomerScreen(closeSheet = onCloseBottomSheet) + } + + is SheetScreen.UpdateCustomer -> { +// AddEditCustomerScreen( +// customerId = current.customerId, +// closeSheet = onCloseBottomSheet +// ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/utils/SheetScreen.kt b/core/ui/src/main/java/com/niyaj/ui/utils/SheetScreen.kt similarity index 94% rename from app/src/main/java/com/niyaj/poposroom/features/common/utils/SheetScreen.kt rename to core/ui/src/main/java/com/niyaj/ui/utils/SheetScreen.kt index 95c5b215..09d15f45 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/utils/SheetScreen.kt +++ b/core/ui/src/main/java/com/niyaj/ui/utils/SheetScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.utils +package com.niyaj.ui.utils sealed class SheetScreen(val type: String) { object CreateNewAddOnItem : SheetScreen("Create New AddOn Item") diff --git a/core/ui/src/main/java/com/niyaj/ui/utils/UiEvent.kt b/core/ui/src/main/java/com/niyaj/ui/utils/UiEvent.kt new file mode 100644 index 00000000..28e5ca53 --- /dev/null +++ b/core/ui/src/main/java/com/niyaj/ui/utils/UiEvent.kt @@ -0,0 +1,7 @@ +package com.niyaj.ui.utils + +sealed class UiEvent { + data class OnSuccess(val successMessage: String) : UiEvent() + + data class OnError(val errorMessage: String) : UiEvent() +} \ No newline at end of file diff --git a/app/src/main/res/drawable/emptycart.webp b/core/ui/src/main/res/drawable/emptycart.webp similarity index 100% rename from app/src/main/res/drawable/emptycart.webp rename to core/ui/src/main/res/drawable/emptycart.webp diff --git a/app/src/main/res/drawable/emptycarttwo.webp b/core/ui/src/main/res/drawable/emptycarttwo.webp similarity index 100% rename from app/src/main/res/drawable/emptycarttwo.webp rename to core/ui/src/main/res/drawable/emptycarttwo.webp diff --git a/app/src/main/res/drawable/emptyorder.webp b/core/ui/src/main/res/drawable/emptyorder.webp similarity index 100% rename from app/src/main/res/drawable/emptyorder.webp rename to core/ui/src/main/res/drawable/emptyorder.webp diff --git a/app/src/main/res/drawable/emptystate.webp b/core/ui/src/main/res/drawable/emptystate.webp similarity index 100% rename from app/src/main/res/drawable/emptystate.webp rename to core/ui/src/main/res/drawable/emptystate.webp diff --git a/app/src/main/res/drawable/emptystatetwo.webp b/core/ui/src/main/res/drawable/emptystatetwo.webp similarity index 100% rename from app/src/main/res/drawable/emptystatetwo.webp rename to core/ui/src/main/res/drawable/emptystatetwo.webp diff --git a/app/src/main/res/drawable/freetime.webp b/core/ui/src/main/res/drawable/freetime.webp similarity index 100% rename from app/src/main/res/drawable/freetime.webp rename to core/ui/src/main/res/drawable/freetime.webp diff --git a/app/src/main/res/drawable/noitems.webp b/core/ui/src/main/res/drawable/noitems.webp similarity index 100% rename from app/src/main/res/drawable/noitems.webp rename to core/ui/src/main/res/drawable/noitems.webp diff --git a/app/src/main/res/drawable/nothinghere.webp b/core/ui/src/main/res/drawable/nothinghere.webp similarity index 100% rename from app/src/main/res/drawable/nothinghere.webp rename to core/ui/src/main/res/drawable/nothinghere.webp diff --git a/app/src/main/res/drawable/outline_cart.xml b/core/ui/src/main/res/drawable/outline_cart.xml similarity index 100% rename from app/src/main/res/drawable/outline_cart.xml rename to core/ui/src/main/res/drawable/outline_cart.xml diff --git a/app/src/main/res/drawable/outline_home.xml b/core/ui/src/main/res/drawable/outline_home.xml similarity index 100% rename from app/src/main/res/drawable/outline_home.xml rename to core/ui/src/main/res/drawable/outline_home.xml diff --git a/app/src/main/res/drawable/outline_orders.xml b/core/ui/src/main/res/drawable/outline_orders.xml similarity index 100% rename from app/src/main/res/drawable/outline_orders.xml rename to core/ui/src/main/res/drawable/outline_orders.xml diff --git a/app/src/main/res/drawable/outline_reports.xml b/core/ui/src/main/res/drawable/outline_reports.xml similarity index 100% rename from app/src/main/res/drawable/outline_reports.xml rename to core/ui/src/main/res/drawable/outline_reports.xml diff --git a/app/src/main/res/drawable/plus.xml b/core/ui/src/main/res/drawable/plus.xml similarity index 100% rename from app/src/main/res/drawable/plus.xml rename to core/ui/src/main/res/drawable/plus.xml diff --git a/core/ui/src/main/res/drawable/popos.webp b/core/ui/src/main/res/drawable/popos.webp new file mode 100644 index 00000000..6709872b Binary files /dev/null and b/core/ui/src/main/res/drawable/popos.webp differ diff --git a/core/ui/src/main/res/drawable/reslogo.jpg b/core/ui/src/main/res/drawable/reslogo.jpg new file mode 100644 index 00000000..00894549 Binary files /dev/null and b/core/ui/src/main/res/drawable/reslogo.jpg differ diff --git a/app/src/main/res/drawable/round_cart.xml b/core/ui/src/main/res/drawable/round_cart.xml similarity index 100% rename from app/src/main/res/drawable/round_cart.xml rename to core/ui/src/main/res/drawable/round_cart.xml diff --git a/app/src/main/res/drawable/round_home.xml b/core/ui/src/main/res/drawable/round_home.xml similarity index 100% rename from app/src/main/res/drawable/round_home.xml rename to core/ui/src/main/res/drawable/round_home.xml diff --git a/app/src/main/res/drawable/round_orders.xml b/core/ui/src/main/res/drawable/round_orders.xml similarity index 100% rename from app/src/main/res/drawable/round_orders.xml rename to core/ui/src/main/res/drawable/round_orders.xml diff --git a/app/src/main/res/drawable/round_reports.xml b/core/ui/src/main/res/drawable/round_reports.xml similarity index 100% rename from app/src/main/res/drawable/round_reports.xml rename to core/ui/src/main/res/drawable/round_reports.xml diff --git a/app/src/main/res/drawable/rounded_rect.xml b/core/ui/src/main/res/drawable/rounded_rect.xml similarity index 100% rename from app/src/main/res/drawable/rounded_rect.xml rename to core/ui/src/main/res/drawable/rounded_rect.xml diff --git a/core/ui/src/main/res/drawable/splash.xml b/core/ui/src/main/res/drawable/splash.xml new file mode 100644 index 00000000..29eda039 --- /dev/null +++ b/core/ui/src/main/res/drawable/splash.xml @@ -0,0 +1,526 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/ui/src/main/res/values/strings.xml b/core/ui/src/main/res/values/strings.xml new file mode 100644 index 00000000..fe77d61c --- /dev/null +++ b/core/ui/src/main/res/values/strings.xml @@ -0,0 +1,15 @@ + + Popos + Restaurant POS Application. + Popos Crash Report. + Your application has-been crashed, check out below attached file for more details. + Application has been crashed + Create New + Create + Show Password + Hide Password + Order Details Icon + Print Order + Popos Highlight + - Pure And Tasty - + \ No newline at end of file diff --git a/core/ui/src/test/java/com/niyaj/ui/ExampleUnitTest.kt b/core/ui/src/test/java/com/niyaj/ui/ExampleUnitTest.kt new file mode 100644 index 00000000..30e7a434 --- /dev/null +++ b/core/ui/src/test/java/com/niyaj/ui/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.ui + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/account/.gitignore b/feature/account/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/account/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/account/build.gradle.kts b/feature/account/build.gradle.kts new file mode 100644 index 00000000..4c4a694e --- /dev/null +++ b/feature/account/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.account" + + ksp { + arg("compose-destinations.moduleName", "account") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/account/proguard-rules.pro b/feature/account/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/account/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/account/src/androidTest/java/com/niyaj/account/ExampleInstrumentedTest.kt b/feature/account/src/androidTest/java/com/niyaj/account/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..0d96aa3a --- /dev/null +++ b/feature/account/src/androidTest/java/com/niyaj/account/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.account + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.account", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/account/src/main/AndroidManifest.xml b/feature/account/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/account/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/account/src/main/java/com/niyaj/account/login/LoginScreen.kt b/feature/account/src/main/java/com/niyaj/account/login/LoginScreen.kt new file mode 100644 index 00000000..d7aafb2f --- /dev/null +++ b/feature/account/src/main/java/com/niyaj/account/login/LoginScreen.kt @@ -0,0 +1,15 @@ +package com.niyaj.account.login + +import androidx.compose.runtime.Composable +import androidx.navigation.NavController +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph + +@Destination +@RootNavGraph(start = true) +@Composable +fun LoginScreen( + navController: NavController, +) { +// Todo: Implement login screen +} \ No newline at end of file diff --git a/feature/account/src/main/java/com/niyaj/account/register/RegisterScreen.kt b/feature/account/src/main/java/com/niyaj/account/register/RegisterScreen.kt new file mode 100644 index 00000000..1b011d61 --- /dev/null +++ b/feature/account/src/main/java/com/niyaj/account/register/RegisterScreen.kt @@ -0,0 +1,13 @@ +package com.niyaj.account.register + +import androidx.compose.runtime.Composable +import androidx.navigation.NavController +import com.ramcosta.composedestinations.annotation.Destination + +@Destination +@Composable +fun RegisterScreen( + navController: NavController, +) { +// Todo: Implement register screen +} \ No newline at end of file diff --git a/feature/account/src/test/java/com/niyaj/account/ExampleUnitTest.kt b/feature/account/src/test/java/com/niyaj/account/ExampleUnitTest.kt new file mode 100644 index 00000000..12574adf --- /dev/null +++ b/feature/account/src/test/java/com/niyaj/account/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.account + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/addonitem/.gitignore b/feature/addonitem/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/addonitem/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/addonitem/build.gradle.kts b/feature/addonitem/build.gradle.kts new file mode 100644 index 00000000..12bedc3d --- /dev/null +++ b/feature/addonitem/build.gradle.kts @@ -0,0 +1,33 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.addonitem" + + ksp { + arg("compose-destinations.moduleName", "addonitem") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.material3.windowSizeClass) + implementation(libs.accompanist.systemuicontroller) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + implementation(libs.timber) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/addonitem/proguard-rules.pro b/feature/addonitem/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/addonitem/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/addonitem/src/androidTest/java/com/niyaj/addonitem/ExampleInstrumentedTest.kt b/feature/addonitem/src/androidTest/java/com/niyaj/addonitem/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..974caaa4 --- /dev/null +++ b/feature/addonitem/src/androidTest/java/com/niyaj/addonitem/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.addonitem + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.addonitem", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/addonitem/src/main/AndroidManifest.xml b/feature/addonitem/src/main/AndroidManifest.xml new file mode 100644 index 00000000..44008a43 --- /dev/null +++ b/feature/addonitem/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/AddOnItemScreen.kt b/feature/addonitem/src/main/java/com/niyaj/addonitem/AddOnItemScreen.kt similarity index 58% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/AddOnItemScreen.kt rename to feature/addonitem/src/main/java/com/niyaj/addonitem/AddOnItemScreen.kt index 0a84cd71..10e1a50e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/AddOnItemScreen.kt +++ b/feature/addonitem/src/main/java/com/niyaj/addonitem/AddOnItemScreen.kt @@ -1,9 +1,9 @@ -package com.niyaj.poposroom.features.addon_item.presentation +package com.niyaj.addonitem import androidx.activity.compose.BackHandler +import androidx.compose.animation.Crossfade import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.ExperimentalFoundationApi -import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -20,10 +20,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Link import androidx.compose.material3.AlertDialog -import androidx.compose.material3.BottomSheetScaffoldState import androidx.compose.material3.CardDefaults import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHostState @@ -42,37 +40,45 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADDON_ITEM_TAG -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADDON_NOT_AVAIlABLE -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.ADDON_SCREEN_TITLE -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.CREATE_NEW_ADD_ON -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.DELETE_ADD_ON_ITEM_MESSAGE -import com.niyaj.poposroom.features.addon_item.domain.utils.AddOnConstants.DELETE_ADD_ON_ITEM_TITLE -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.Constants.SEARCH_ITEM_NOT_FOUND -import com.niyaj.poposroom.features.common.utils.SheetScreen -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.common.utils.toRupee +import com.niyaj.addonitem.destinations.AddEditAddOnItemScreenDestination +import com.niyaj.common.utils.Constants.SEARCH_ITEM_NOT_FOUND +import com.niyaj.common.utils.toRupee +import com.niyaj.data.utils.AddOnTestTags.ADDON_ITEM_TAG +import com.niyaj.data.utils.AddOnTestTags.ADDON_NOT_AVAIlABLE +import com.niyaj.data.utils.AddOnTestTags.ADDON_SCREEN_TITLE +import com.niyaj.data.utils.AddOnTestTags.ADDON_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.AddOnTestTags.CREATE_NEW_ADD_ON +import com.niyaj.data.utils.AddOnTestTags.DELETE_ADD_ON_ITEM_MESSAGE +import com.niyaj.data.utils.AddOnTestTags.DELETE_ADD_ON_ITEM_TITLE +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.AddOnItem +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.components.drawAnimatedBorder +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.ramcosta.composedestinations.navigation.navigate +import com.ramcosta.composedestinations.result.NavResult +import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.AddOnItemScreen +) @Composable fun AddOnItemScreen( - bottomSheetScaffoldState: BottomSheetScaffoldState, navController: NavController, - onCloseSheet: () -> Unit = {}, - onOpenSheet: (SheetScreen) -> Unit = {}, viewModel: AddOnViewModel = hiltViewModel(), + resultRecipient: ResultRecipient, ) { val scope = rememberCoroutineScope() val snackbarState = remember { SnackbarHostState() } @@ -91,15 +97,30 @@ fun AddOnItemScreen( val openDialog = remember { mutableStateOf(false) } + resultRecipient.onNavResult { result -> + when (result) { + is NavResult.Canceled -> { + viewModel.deselectItems() + } + + is NavResult.Value -> { + viewModel.deselectItems() + scope.launch { + snackbarState.showSnackbar(result.value) + } + } + } + } + LaunchedEffect(key1 = event) { event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} + when (data) { is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) } } + is UiEvent.OnSuccess -> { scope.launch { snackbarState.showSnackbar(data.successMessage) @@ -115,26 +136,18 @@ fun AddOnItemScreen( } else if (showSearchBar) { viewModel.closeSearchBar() } - if (bottomSheetScaffoldState.bottomSheetState.hasExpandedState) { - onCloseSheet() - } } StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) ADDON_SCREEN_TITLE else "${selectedItems.size} Selected", - showSearchBar = showSearchBar, - selectionCount = selectedItems.size, - searchText = searchText, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyGridState.isScrolled, fabText = CREATE_NEW_ADD_ON, fabVisible = (showFab && selectedItems.isEmpty() && !showSearchBar), onFabClick = { - onOpenSheet(SheetScreen.CreateNewAddOnItem) + navController.navigate(AddEditAddOnItemScreenDestination()) }, onClickScroll = { scope.launch { @@ -143,54 +156,76 @@ fun AddOnItemScreen( } ) }, - fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - onOpenSheet(SheetScreen.UpdateAddOnItem(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true + navActions = { + ScaffoldNavActions( + placeholderText = ADDON_SEARCH_PLACEHOLDER, + showSettingsIcon = true, + selectionCount = selectedItems.size, + showSearchIcon = showSearchBar, + searchText = searchText, + onEditClick = { + navController.navigate(AddEditAddOnItemScreenDestination(selectedItems.first())) + }, + onDeleteClick = { + openDialog.value = true + }, + onSettingsClick = { + + }, + onSelectAllClick = viewModel::selectAllItems, + onClearClick = viewModel::clearSearchText, + onSearchClick = viewModel::openSearchBar, + onSearchTextChanged = viewModel::searchTextChanged + ) }, + fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, - onSearchTextChanged = viewModel::searchTextChanged, - onSearchClick = viewModel::openSearchBar, onBackClick = viewModel::closeSearchBar, - onClearClick = viewModel::clearSearchText + snackbarHostState = snackbarState, ) { _ -> - when (state) { - is UiState.Loading -> LoadingIndicator() - is UiState.Empty -> { - ItemNotAvailable( - text = if (searchText.isEmpty()) ADDON_NOT_AVAIlABLE else SEARCH_ITEM_NOT_FOUND, - buttonText = CREATE_NEW_ADD_ON, - onClick = { - onOpenSheet(SheetScreen.CreateNewAddOnItem) - } - ) - } - is UiState.Success -> { - LazyVerticalGrid( - modifier = Modifier - .padding(SpaceSmall), - columns = GridCells.Fixed(2), - state = lazyGridState, - ) { - items( - items = state.data, - key = { it.itemId} - ) { item: AddOnItem -> - AddOnItemData( - item = item, - doesSelected = { - selectedItems.contains(it) - }, - onClick = { - if (selectedItems.isNotEmpty()) { - viewModel.selectItem(it) - } - }, - onLongClick = viewModel::selectItem - ) + Crossfade( + targetState = state, + label = "AddOn State" + ) { state -> + when (state) { + is UiState.Loading -> LoadingIndicator() + + is UiState.Empty -> { + ItemNotAvailable( + text = if (searchText.isEmpty()) ADDON_NOT_AVAIlABLE else SEARCH_ITEM_NOT_FOUND, + buttonText = CREATE_NEW_ADD_ON, + onClick = { + navController.navigate(AddEditAddOnItemScreenDestination()) + } + ) + } + + is UiState.Success -> { + LazyVerticalGrid( + modifier = Modifier + .padding(SpaceSmall), + columns = GridCells.Fixed(2), + state = lazyGridState, + ) { + items( + items = state.data, + key = { it.itemId } + ) { item: AddOnItem -> + AddOnItemData( + item = item, + doesSelected = { + selectedItems.contains(it) + }, + onClick = { + if (selectedItems.isNotEmpty()) { + viewModel.selectItem(it) + } + }, + onLongClick = viewModel::selectItem + ) + } } } } @@ -254,7 +289,12 @@ fun AddOnItemData( .testTag(ADDON_ITEM_TAG.plus(item.itemId)) .padding(SpaceSmall) .then(borderStroke?.let { - Modifier.border(it, CardDefaults.elevatedShape) + Modifier + .drawAnimatedBorder( + strokeWidth = 1.dp, + durationMillis = 2000, + shape = CardDefaults.elevatedShape + ) } ?: Modifier) .clip(CardDefaults.elevatedShape) .combinedClickable( @@ -265,6 +305,9 @@ fun AddOnItemData( onLongClick(item.itemId) }, ), + elevation = CardDefaults.elevatedCardElevation( + defaultElevation = 2.dp + ) ) { Row( modifier = Modifier diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/AddOnViewModel.kt b/feature/addonitem/src/main/java/com/niyaj/addonitem/AddOnViewModel.kt similarity index 79% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/AddOnViewModel.kt rename to feature/addonitem/src/main/java/com/niyaj/addonitem/AddOnViewModel.kt index c0dc9315..e1e9e76d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/AddOnViewModel.kt +++ b/feature/addonitem/src/main/java/com/niyaj/addonitem/AddOnViewModel.kt @@ -1,14 +1,14 @@ -package com.niyaj.poposroom.features.addon_item.presentation +package com.niyaj.addonitem import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.addon_item.domain.repository.AddOnItemRepository -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent +import com.niyaj.common.result.Resource +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.data.repository.AddOnItemRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -24,7 +24,7 @@ import javax.inject.Inject class AddOnViewModel @Inject constructor( private val itemRepository: AddOnItemRepository, @Dispatcher(PoposDispatchers.IO) private val ioDispatcher: CoroutineDispatcher -): BaseViewModel() { +) : BaseViewModel() { override var totalItems: List = emptyList() @@ -52,7 +52,7 @@ class AddOnViewModel @Inject constructor( val result = itemRepository.deleteAddOnItems(selectedItems.toList()) mSelectedItems.clear() - when(result) { + when (result) { is Resource.Success -> { mEventFlow.emit( UiEvent.OnSuccess( @@ -60,6 +60,7 @@ class AddOnViewModel @Inject constructor( ) ) } + is Resource.Error -> { mEventFlow.emit( UiEvent.OnError(result.message ?: "Unable to delete items") diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemEvent.kt b/feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemEvent.kt similarity index 69% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemEvent.kt rename to feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemEvent.kt index a4f3d3f8..a2871ab5 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemEvent.kt +++ b/feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemEvent.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.addon_item.presentation.add_edit +package com.niyaj.addonitem.add_edit sealed class AddEditAddOnItemEvent { @@ -6,7 +6,7 @@ sealed class AddEditAddOnItemEvent { data class ItemPriceChanged(val itemPrice: String) : AddEditAddOnItemEvent() - object ItemApplicableChanged: AddEditAddOnItemEvent() + data object ItemApplicableChanged : AddEditAddOnItemEvent() data class CreateUpdateAddOnItem(val addOnItemId: Int = 0) : AddEditAddOnItemEvent() } diff --git a/feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemScreen.kt b/feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemScreen.kt new file mode 100644 index 00000000..850e4f41 --- /dev/null +++ b/feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemScreen.kt @@ -0,0 +1,157 @@ +package com.niyaj.addonitem.add_edit + +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.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Category +import androidx.compose.material.icons.filled.CurrencyRupee +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.Checkbox +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.input.KeyboardType +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import com.niyaj.common.utils.safeString +import com.niyaj.data.utils.AddOnTestTags.ADDON_APPLIED_SWITCH +import com.niyaj.data.utils.AddOnTestTags.ADDON_NAME_ERROR_TAG +import com.niyaj.data.utils.AddOnTestTags.ADDON_NAME_FIELD +import com.niyaj.data.utils.AddOnTestTags.ADDON_PRICE_ERROR_TAG +import com.niyaj.data.utils.AddOnTestTags.ADDON_PRICE_FIELD +import com.niyaj.data.utils.AddOnTestTags.ADD_EDIT_ADDON_BUTTON +import com.niyaj.data.utils.AddOnTestTags.ADD_EDIT_ADDON_SCREEN +import com.niyaj.data.utils.AddOnTestTags.CREATE_NEW_ADD_ON +import com.niyaj.data.utils.AddOnTestTags.EDIT_ADD_ON_ITEM +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.result.ResultBackNavigator + +@Destination( + route = Screens.AddEditAddOnItemScreen +) +@Composable +fun AddEditAddOnItemScreen( + itemId: Int = 0, + navController: NavController, + viewModel: AddEditAddOnItemViewModel = hiltViewModel(), + resultBackNavigator: ResultBackNavigator +) { + val nameError = viewModel.nameError.collectAsStateWithLifecycle().value + val priceError = viewModel.priceError.collectAsStateWithLifecycle().value + + val enableBtn = nameError == null && priceError == null + + val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value + + LaunchedEffect(key1 = event) { + event?.let { data -> + when (data) { + is UiEvent.OnError -> { + resultBackNavigator.navigateBack(data.errorMessage) + } + + is UiEvent.OnSuccess -> { + resultBackNavigator.navigateBack(data.successMessage) + } + } + } + } + + val title = if (itemId == 0) CREATE_NEW_ADD_ON else EDIT_ADD_ON_ITEM + + StandardScaffoldWithOutDrawer( + title = title, + onBackClick = { + navController.navigateUp() + }, + showBottomBar = enableBtn, + bottomBar = { + StandardButton( + modifier = Modifier + .testTag(ADD_EDIT_ADDON_BUTTON) + .padding(horizontal = SpaceSmallMax), + text = title, + enabled = enableBtn, + icon = if (itemId == 0) Icons.Default.Add else Icons.Default.Edit, + onClick = { + viewModel.onEvent(AddEditAddOnItemEvent.CreateUpdateAddOnItem(itemId)) + } + ) + } + ) { + Column( + modifier = Modifier + .testTag(ADD_EDIT_ADDON_SCREEN) + .fillMaxWidth() + .padding(SpaceSmall), + verticalArrangement = Arrangement.spacedBy(SpaceSmall, Alignment.CenterVertically), + ) { + StandardOutlinedTextField( + value = viewModel.addEditState.itemName, + label = ADDON_NAME_FIELD, + leadingIcon = Icons.Default.Category, + isError = nameError != null, + errorText = nameError, + errorTextTag = ADDON_NAME_ERROR_TAG, + onValueChange = { + viewModel.onEvent(AddEditAddOnItemEvent.ItemNameChanged(it)) + } + ) + + StandardOutlinedTextField( + value = viewModel.addEditState.itemPrice.safeString, + label = ADDON_PRICE_FIELD, + leadingIcon = Icons.Default.CurrencyRupee, + isError = priceError != null, + errorText = priceError, + keyboardType = KeyboardType.Number, + errorTextTag = ADDON_PRICE_ERROR_TAG, + onValueChange = { + viewModel.onEvent(AddEditAddOnItemEvent.ItemPriceChanged(it)) + } + ) + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Checkbox( + modifier = Modifier.testTag(ADDON_APPLIED_SWITCH), + checked = viewModel.addEditState.isApplicable, + onCheckedChange = { + viewModel.onEvent(AddEditAddOnItemEvent.ItemApplicableChanged) + } + ) + Spacer(modifier = Modifier.width(SpaceSmall)) + Text( + text = if (viewModel.addEditState.isApplicable) + "Marked as applied" + else + "Marked as not applied", + style = MaterialTheme.typography.labelMedium + ) + } + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemState.kt b/feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemState.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemState.kt rename to feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemState.kt index 1eb77c64..f54d22af 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemState.kt +++ b/feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemState.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.addon_item.presentation.add_edit +package com.niyaj.addonitem.add_edit data class AddEditAddOnItemState( val itemName: String = "", diff --git a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemViewModel.kt b/feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemViewModel.kt similarity index 68% rename from app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemViewModel.kt rename to feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemViewModel.kt index 442ca8f5..806db2a9 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/addon_item/presentation/add_edit/AddEditAddOnItemViewModel.kt +++ b/feature/addonitem/src/main/java/com/niyaj/addonitem/add_edit/AddEditAddOnItemViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.addon_item.presentation.add_edit +package com.niyaj.addonitem.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,16 +7,13 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.addon_item.domain.repository.AddOnItemRepository -import com.niyaj.poposroom.features.addon_item.domain.repository.AddOnItemValidationRepository -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.safeInt +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.safeInt +import com.niyaj.data.repository.AddOnItemRepository +import com.niyaj.data.repository.validation.AddOnItemValidationRepository +import com.niyaj.model.AddOnItem +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharingStarted @@ -33,11 +30,10 @@ import javax.inject.Inject class AddEditAddOnItemViewModel @Inject constructor( private val repository: AddOnItemRepository, private val validationRepository: AddOnItemValidationRepository, - @Dispatcher(PoposDispatchers.IO) private val ioDispatcher: CoroutineDispatcher, - savedStateHandle: SavedStateHandle + savedStateHandle: SavedStateHandle, ) : ViewModel() { - private var addOnItemId = savedStateHandle.get("addOnItemId") + private val addOnItemId = savedStateHandle.get("itemId") ?: 0 var addEditState by mutableStateOf(AddEditAddOnItemState()) @@ -45,7 +41,7 @@ class AddEditAddOnItemViewModel @Inject constructor( val eventFlow = _eventFlow.asSharedFlow() init { - savedStateHandle.get("addOnItemId")?.let { addOnItemId -> + savedStateHandle.get("itemId")?.let { addOnItemId -> getAllAddOnItemById(addOnItemId) } } @@ -88,33 +84,30 @@ class AddEditAddOnItemViewModel @Inject constructor( } } - fun getAllAddOnItemById(itemId: Int) { - viewModelScope.launch(ioDispatcher) { - when (val result = repository.getAddOnItemById(itemId)) { - is Resource.Success -> { - result.data?.let { addOnItem -> - addOnItemId = addOnItem.itemId - - addEditState = addEditState.copy( - itemName = addOnItem.itemName, - itemPrice = addOnItem.itemPrice, - isApplicable = addOnItem.isApplicable + private fun getAllAddOnItemById(itemId: Int) { + if (itemId != 0) { + viewModelScope.launch { + when (val result = repository.getAddOnItemById(itemId)) { + is Resource.Success -> { + result.data?.let { addOnItem -> + addEditState = addEditState.copy( + itemName = addOnItem.itemName, + itemPrice = addOnItem.itemPrice, + isApplicable = addOnItem.isApplicable + ) + } + } + + is Resource.Error -> { + _eventFlow.emit( + UiEvent.OnError(result.message ?: "Unable to get addon item") ) } } - is Resource.Error -> { - _eventFlow.emit( - UiEvent.OnError(result.message ?: "Unable to get addon item") - ) - } } } } - fun resetFields() { - addEditState = AddEditAddOnItemState() - } - private fun createOrUpdateAddOnItem(addOnItemId: Int = 0) { viewModelScope.launch { if (nameError.value == null && priceError.value == null) { @@ -137,7 +130,7 @@ class AddEditAddOnItemViewModel @Inject constructor( } is Resource.Success -> { - _eventFlow.emit(UiEvent.OnSuccess("AddOn Item Created Successfully.")) + _eventFlow.emit(UiEvent.OnSuccess("AddOn Item Created Or Updated Successfully.")) } } diff --git a/feature/addonitem/src/main/res/values/strings.xml b/feature/addonitem/src/main/res/values/strings.xml new file mode 100644 index 00000000..82c1b43f --- /dev/null +++ b/feature/addonitem/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + AddOnItem + \ No newline at end of file diff --git a/feature/addonitem/src/test/java/com/niyaj/addonitem/ExampleUnitTest.kt b/feature/addonitem/src/test/java/com/niyaj/addonitem/ExampleUnitTest.kt new file mode 100644 index 00000000..1aee93e5 --- /dev/null +++ b/feature/addonitem/src/test/java/com/niyaj/addonitem/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.addonitem + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/address/.gitignore b/feature/address/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/address/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/address/build.gradle.kts b/feature/address/build.gradle.kts new file mode 100644 index 00000000..d71681a2 --- /dev/null +++ b/feature/address/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.address" + + ksp { + arg("compose-destinations.moduleName", "address") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/address/proguard-rules.pro b/feature/address/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/address/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/address/src/androidTest/java/com/niyaj/address/ExampleInstrumentedTest.kt b/feature/address/src/androidTest/java/com/niyaj/address/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..417e7df8 --- /dev/null +++ b/feature/address/src/androidTest/java/com/niyaj/address/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.address + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.address", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/address/src/main/AndroidManifest.xml b/feature/address/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/address/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/AddressScreen.kt b/feature/address/src/main/java/com/niyaj/address/AddressScreen.kt similarity index 59% rename from app/src/main/java/com/niyaj/poposroom/features/address/presentation/AddressScreen.kt rename to feature/address/src/main/java/com/niyaj/address/AddressScreen.kt index a094f1d1..322acde5 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/AddressScreen.kt +++ b/feature/address/src/main/java/com/niyaj/address/AddressScreen.kt @@ -1,6 +1,7 @@ -package com.niyaj.poposroom.features.address.presentation +package com.niyaj.address import androidx.activity.compose.BackHandler +import androidx.compose.animation.Crossfade import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.border @@ -20,10 +21,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Business import androidx.compose.material3.AlertDialog -import androidx.compose.material3.BottomSheetScaffoldState import androidx.compose.material3.CardDefaults import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHostState @@ -42,40 +41,47 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.ADDRESS_ITEM_TAG -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.ADDRESS_NOT_AVAIlABLE -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.ADDRESS_SCREEN_TITLE -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.CREATE_NEW_ADDRESS -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.DELETE_ADDRESS_ITEM_MESSAGE -import com.niyaj.poposroom.features.address.domain.utils.AddressTestTags.DELETE_ADDRESS_ITEM_TITLE -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.Constants.SEARCH_ITEM_NOT_FOUND -import com.niyaj.poposroom.features.common.utils.SheetScreen -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled +import com.niyaj.address.destinations.AddEditAddressScreenDestination +import com.niyaj.common.utils.Constants.SEARCH_ITEM_NOT_FOUND +import com.niyaj.data.utils.AddressTestTags.ADDRESS_ITEM_TAG +import com.niyaj.data.utils.AddressTestTags.ADDRESS_NOT_AVAIlABLE +import com.niyaj.data.utils.AddressTestTags.ADDRESS_SCREEN_TITLE +import com.niyaj.data.utils.AddressTestTags.ADDRESS_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.AddressTestTags.CREATE_NEW_ADDRESS +import com.niyaj.data.utils.AddressTestTags.DELETE_ADDRESS_ITEM_MESSAGE +import com.niyaj.data.utils.AddressTestTags.DELETE_ADDRESS_ITEM_TITLE +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.Address +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.ramcosta.composedestinations.navigation.navigate +import com.ramcosta.composedestinations.result.NavResult +import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.AddressScreen +) @Composable fun AddressScreen( - bottomSheetScaffoldState: BottomSheetScaffoldState, navController: NavController, - onCloseSheet: () -> Unit = {}, - onOpenSheet: (SheetScreen) -> Unit = {}, viewModel: AddressViewModel = hiltViewModel(), + resultRecipient: ResultRecipient, ) { val scope = rememberCoroutineScope() val snackbarState = remember { SnackbarHostState() } - val state = viewModel.addOnItems.collectAsStateWithLifecycle().value + val uiState = viewModel.addOnItems.collectAsStateWithLifecycle().value val selectedItems = viewModel.selectedItems.toList() @@ -92,13 +98,13 @@ fun AddressScreen( LaunchedEffect(key1 = event) { event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} + when (data) { is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) } } + is UiEvent.OnSuccess -> { scope.launch { snackbarState.showSnackbar(data.successMessage) @@ -114,26 +120,33 @@ fun AddressScreen( } else if (showSearchBar) { viewModel.closeSearchBar() } - if (bottomSheetScaffoldState.bottomSheetState.hasExpandedState) { - onCloseSheet() + } + + resultRecipient.onNavResult { result -> + when (result) { + is NavResult.Canceled -> { + viewModel.deselectItems() + } + + is NavResult.Value -> { + scope.launch { + viewModel.deselectItems() + snackbarState.showSnackbar(result.value) + } + } } } StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) ADDRESS_SCREEN_TITLE else "${selectedItems.size} Selected", - showSearchBar = showSearchBar, - selectionCount = selectedItems.size, - searchText = searchText, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyGridState.isScrolled, fabText = CREATE_NEW_ADDRESS, fabVisible = (showFab && selectedItems.isEmpty() && !showSearchBar), onFabClick = { - onOpenSheet(SheetScreen.CreateNewAddress) + navController.navigate(AddEditAddressScreenDestination()) }, onClickScroll = { scope.launch { @@ -142,57 +155,78 @@ fun AddressScreen( } ) }, - fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - onOpenSheet(SheetScreen.UpdateAddress(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true + navActions = { + ScaffoldNavActions( + placeholderText = ADDRESS_SEARCH_PLACEHOLDER, + showSettingsIcon = true, + selectionCount = selectedItems.size, + showSearchIcon = showSearchBar, + searchText = searchText, + onEditClick = { + navController.navigate(AddEditAddressScreenDestination(selectedItems.first())) + }, + onDeleteClick = { + openDialog.value = true + }, + onSettingsClick = { + + }, + onSelectAllClick = viewModel::selectAllItems, + onClearClick = viewModel::clearSearchText, + onSearchClick = viewModel::openSearchBar, + onSearchTextChanged = viewModel::searchTextChanged + ) }, + fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, - onSearchTextChanged = viewModel::searchTextChanged, - onSearchClick = viewModel::openSearchBar, onBackClick = viewModel::closeSearchBar, - onClearClick = viewModel::clearSearchText + snackbarHostState = snackbarState, ) { _ -> - when (state) { - is UiState.Loading -> LoadingIndicator() - is UiState.Empty -> { - ItemNotAvailable( - text = if (searchText.isEmpty()) ADDRESS_NOT_AVAIlABLE else SEARCH_ITEM_NOT_FOUND, - buttonText = CREATE_NEW_ADDRESS, - onClick = { - onOpenSheet(SheetScreen.CreateNewAddress) - } - ) - } - is UiState.Success -> { - LazyVerticalGrid( - modifier = Modifier - .padding(SpaceSmall), - columns = GridCells.Fixed(2), - state = lazyGridState, - ) { - items( - items = state.data, - key = { - it.addressName.plus(it.addressId) + Crossfade( + targetState = uiState, + label = "Address State" + ) { state -> + when (state) { + is UiState.Loading -> LoadingIndicator() + is UiState.Empty -> { + ItemNotAvailable( + text = if (searchText.isEmpty()) ADDRESS_NOT_AVAIlABLE else SEARCH_ITEM_NOT_FOUND, + buttonText = CREATE_NEW_ADDRESS, + onClick = { + navController.navigate(AddEditAddressScreenDestination()) + } + ) + } + + is UiState.Success -> { + LazyVerticalGrid( + modifier = Modifier + .padding(SpaceSmall), + columns = GridCells.Fixed(2), + state = lazyGridState, + ) { + items( + items = state.data, + key = { + it.addressName.plus(it.addressId) + } + ) { address -> + AddressData( + modifier = Modifier.testTag(ADDRESS_ITEM_TAG.plus(address.addressId)), + item = address, + doesSelected = { + selectedItems.contains(it) + }, + onClick = { + if (selectedItems.isNotEmpty()) { + viewModel.selectItem(it) + } + }, + onLongClick = viewModel::selectItem + ) } - ) { address -> - AddressData( - modifier = Modifier.testTag(ADDRESS_ITEM_TAG.plus(address.addressId)), - item = address, - doesSelected = { - selectedItems.contains(it) - }, - onClick = { - if (selectedItems.isNotEmpty()) { - viewModel.selectItem(it) - } - }, - onLongClick = viewModel::selectItem - ) } } } @@ -211,7 +245,8 @@ fun AddressScreen( }, text = { Text( - text = DELETE_ADDRESS_ITEM_MESSAGE) + text = DELETE_ADDRESS_ITEM_MESSAGE + ) }, confirmButton = { TextButton( diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/AddressViewModel.kt b/feature/address/src/main/java/com/niyaj/address/AddressViewModel.kt similarity index 77% rename from app/src/main/java/com/niyaj/poposroom/features/address/presentation/AddressViewModel.kt rename to feature/address/src/main/java/com/niyaj/address/AddressViewModel.kt index dbd2c698..dc91a86a 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/AddressViewModel.kt +++ b/feature/address/src/main/java/com/niyaj/address/AddressViewModel.kt @@ -1,14 +1,14 @@ -package com.niyaj.poposroom.features.address.presentation +package com.niyaj.address import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.address.domain.repository.AddressRepository -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.AddressRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -23,7 +23,8 @@ import javax.inject.Inject @HiltViewModel class AddressViewModel @Inject constructor( private val addressRepository: AddressRepository, - @Dispatcher(PoposDispatchers.IO) private val ioDispatcher: CoroutineDispatcher + @Dispatcher(PoposDispatchers.IO) + private val ioDispatcher: CoroutineDispatcher ): BaseViewModel() { override var totalItems: List = emptyList() diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressEvent.kt b/feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressEvent.kt similarity index 80% rename from app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressEvent.kt rename to feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressEvent.kt index 2e389cdb..0f9f30cb 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressEvent.kt +++ b/feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressEvent.kt @@ -1,6 +1,7 @@ -package com.niyaj.poposroom.features.address.presentation.add_edit +package com.niyaj.address.add_edit sealed interface AddEditAddressEvent { + data class ShortNameChanged(val shortName: String) : AddEditAddressEvent data class AddressNameChanged(val addressName: String) : AddEditAddressEvent diff --git a/feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressScreen.kt b/feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressScreen.kt new file mode 100644 index 00000000..61cc4a90 --- /dev/null +++ b/feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressScreen.kt @@ -0,0 +1,123 @@ +package com.niyaj.address.add_edit + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +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.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Business +import androidx.compose.material.icons.filled.CurrencyRupee +import androidx.compose.material.icons.filled.EditLocationAlt +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import com.niyaj.data.utils.AddressTestTags.ADDRESS_FULL_NAME_ERROR +import com.niyaj.data.utils.AddressTestTags.ADDRESS_FULL_NAME_FIELD +import com.niyaj.data.utils.AddressTestTags.ADDRESS_SHORT_NAME_ERROR +import com.niyaj.data.utils.AddressTestTags.ADDRESS_SHORT_NAME_FIELD +import com.niyaj.data.utils.AddressTestTags.ADD_EDIT_ADDRESS_BTN +import com.niyaj.data.utils.AddressTestTags.CREATE_ADDRESS_SCREEN +import com.niyaj.data.utils.AddressTestTags.CREATE_NEW_ADDRESS +import com.niyaj.data.utils.AddressTestTags.EDIT_ADDRESS +import com.niyaj.data.utils.AddressTestTags.UPDATE_ADDRESS_SCREEN +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.components.StandardTextField +import com.niyaj.ui.utils.UiEvent +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.result.ResultBackNavigator + +@Destination +@Composable +fun AddEditAddressScreen( + addressId: Int = 0, + navController: NavController, + viewModel: AddEditAddressViewModel = hiltViewModel(), + resultBackNavigator: ResultBackNavigator, +) { + val nameError = viewModel.nameError.collectAsStateWithLifecycle().value + val shortNameError = viewModel.shortNameError.collectAsStateWithLifecycle().value + + val enableBtn = nameError == null && shortNameError == null + + val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value + + LaunchedEffect(key1 = event) { + event?.let { data -> + when (data) { + is UiEvent.OnError -> { + resultBackNavigator.navigateBack(data.errorMessage) + } + + is UiEvent.OnSuccess -> { + resultBackNavigator.navigateBack(data.successMessage) + } + } + } + } + + val title = if (addressId == 0) CREATE_ADDRESS_SCREEN else UPDATE_ADDRESS_SCREEN + + StandardScaffoldWithOutDrawer( + title = title, + onBackClick = { + navController.navigateUp() + }, + showBottomBar = enableBtn, + bottomBar = { + StandardButton( + modifier = Modifier + .testTag(ADD_EDIT_ADDRESS_BTN) + .padding(horizontal = SpaceSmallMax), + text = if (addressId == 0) CREATE_NEW_ADDRESS else EDIT_ADDRESS, + enabled = enableBtn, + icon = if (addressId == 0) Icons.Default.Add else Icons.Default.EditLocationAlt, + onClick = { + viewModel.onEvent(AddEditAddressEvent.CreateOrUpdateAddress(addressId)) + } + ) + } + ) { + Column( + modifier = Modifier + .testTag(title) + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(SpaceSmall), + ) { + StandardTextField( + value = viewModel.state.addressName, + label = ADDRESS_FULL_NAME_FIELD, + leadingIcon = Icons.Default.Business, + isError = nameError != null, + errorText = nameError, + errorTextTag = ADDRESS_FULL_NAME_ERROR, + onValueChange = { + viewModel.onEvent(AddEditAddressEvent.AddressNameChanged(it)) + } + ) + + StandardTextField( + value = viewModel.state.shortName, + label = ADDRESS_SHORT_NAME_FIELD, + leadingIcon = Icons.Default.CurrencyRupee, + isError = shortNameError != null, + errorText = shortNameError, + errorTextTag = ADDRESS_SHORT_NAME_ERROR, + onValueChange = { + viewModel.onEvent(AddEditAddressEvent.ShortNameChanged(it)) + } + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressState.kt b/feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressState.kt similarity index 73% rename from app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressState.kt rename to feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressState.kt index b74a6d04..c4f95e4b 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressState.kt +++ b/feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressState.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.address.presentation.add_edit +package com.niyaj.address.add_edit data class AddEditAddressState( val shortName: String = "", diff --git a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressViewModel.kt b/feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressViewModel.kt similarity index 81% rename from app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressViewModel.kt rename to feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressViewModel.kt index 8409f204..440fc4f8 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/address/presentation/add_edit/AddEditAddressViewModel.kt +++ b/feature/address/src/main/java/com/niyaj/address/add_edit/AddEditAddressViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.address.presentation.add_edit +package com.niyaj.address.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,14 +7,14 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.address.domain.repository.AddressRepository -import com.niyaj.poposroom.features.address.domain.repository.AddressValidationRepository -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.getAllCapitalizedLetters +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.getAllCapitalizedLetters +import com.niyaj.data.repository.AddressRepository +import com.niyaj.data.repository.validation.AddressValidationRepository +import com.niyaj.model.Address +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -34,8 +34,8 @@ class AddEditAddressViewModel @Inject constructor( private val addressRepository: AddressRepository, private val validationRepository: AddressValidationRepository, @Dispatcher(PoposDispatchers.IO) private val ioDispatcher: CoroutineDispatcher, - savedStateHandle: SavedStateHandle -): ViewModel() { + savedStateHandle: SavedStateHandle, +) : ViewModel() { private var addressId = savedStateHandle.get("addressId") @@ -70,7 +70,7 @@ class AddEditAddressViewModel @Inject constructor( ) fun onEvent(event: AddEditAddressEvent) { - when(event) { + when (event) { is AddEditAddressEvent.AddressNameChanged -> { state = state.copy( addressName = event.addressName, @@ -100,10 +100,11 @@ class AddEditAddressViewModel @Inject constructor( updatedAt = if (addressId != 0) Date() else null ) - when(val result = addressRepository.upsertAddress(address)) { + when (val result = addressRepository.upsertAddress(address)) { is Resource.Error -> { _eventFlow.emit(UiEvent.OnError(result.message!!)) } + is Resource.Success -> { _eventFlow.emit( UiEvent.OnSuccess("Address Created Successfully.") @@ -116,12 +117,13 @@ class AddEditAddressViewModel @Inject constructor( } } - fun getAddressById(itemId: Int){ + private fun getAddressById(itemId: Int) { viewModelScope.launch(ioDispatcher) { - when(val result = addressRepository.getAddressById(itemId)) { + when (val result = addressRepository.getAddressById(itemId)) { is Resource.Error -> { _eventFlow.emit(UiEvent.OnError("Unable to find address")) } + is Resource.Success -> { result.data?.let { address -> addressId = address.addressId @@ -135,9 +137,5 @@ class AddEditAddressViewModel @Inject constructor( } } } - - fun resetFields() { - state = AddEditAddressState() - } - + } \ No newline at end of file diff --git a/feature/address/src/main/res/values/strings.xml b/feature/address/src/main/res/values/strings.xml new file mode 100644 index 00000000..6891378f --- /dev/null +++ b/feature/address/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Address + \ No newline at end of file diff --git a/feature/address/src/test/java/com/niyaj/address/ExampleUnitTest.kt b/feature/address/src/test/java/com/niyaj/address/ExampleUnitTest.kt new file mode 100644 index 00000000..cb96a0eb --- /dev/null +++ b/feature/address/src/test/java/com/niyaj/address/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.address + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/cart/.gitignore b/feature/cart/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/cart/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/cart/build.gradle.kts b/feature/cart/build.gradle.kts new file mode 100644 index 00000000..b8ec3bed --- /dev/null +++ b/feature/cart/build.gradle.kts @@ -0,0 +1,31 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.cart" + + ksp { + arg("compose-destinations.moduleName", "cart") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.permissions) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/cart/proguard-rules.pro b/feature/cart/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/cart/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/cart/src/androidTest/java/com/niyaj/cart/ExampleInstrumentedTest.kt b/feature/cart/src/androidTest/java/com/niyaj/cart/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..aadbd286 --- /dev/null +++ b/feature/cart/src/androidTest/java/com/niyaj/cart/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.cart + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.cart", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/cart/src/main/AndroidManifest.xml b/feature/cart/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/cart/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/cart/src/main/java/com/niyaj/cart/CartScreen.kt b/feature/cart/src/main/java/com/niyaj/cart/CartScreen.kt new file mode 100644 index 00000000..0c26d70f --- /dev/null +++ b/feature/cart/src/main/java/com/niyaj/cart/CartScreen.kt @@ -0,0 +1,85 @@ +package com.niyaj.cart + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Inventory2 +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.navigation.NavController +import com.niyaj.cart.components.CartTabItem +import com.niyaj.cart.components.Tabs +import com.niyaj.cart.components.TabsContent +import com.niyaj.cart.dine_in.DineInScreen +import com.niyaj.cart.dine_out.DineOutScreen +import com.niyaj.ui.components.StandardScaffoldWithBottomNavigation +import com.niyaj.ui.utils.Screens +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph + +@OptIn(ExperimentalFoundationApi::class) +@RootNavGraph(start = true) +@Destination( + route = Screens.CartScreen +) +@Composable +fun CartScreen( + navController: NavController, + onClickEditOrder: (Int) -> Unit, + onClickOrderDetails: (Int) -> Unit, + onNavigateToOrderScreen: () -> Unit, +) { + val pagerState = rememberPagerState( + initialPage = 0, + initialPageOffsetFraction = 0f, + pageCount = { 2 } + ) + + StandardScaffoldWithBottomNavigation( + navController = navController, + title = "My Cart", + navActions = { + IconButton( + onClick = onNavigateToOrderScreen, + ) { + Icon( + imageVector = Icons.Outlined.Inventory2, + contentDescription = "go to order screen", + ) + } + }, + bottomBar = {}, + ) { + val tabs = listOf( + CartTabItem.DineOutItem { + DineOutScreen( + navController = navController, + onClickEditOrder = onClickEditOrder, + onClickOrderDetails = onClickOrderDetails, + onNavigateToOrderScreen = onNavigateToOrderScreen, + ) + }, + CartTabItem.DineInItem { + DineInScreen( + navController = navController, + onClickEditOrder = onClickEditOrder, + onClickOrderDetails = onClickOrderDetails, + onNavigateToOrderScreen = onNavigateToOrderScreen, + ) + }, + ) + + Column( + modifier = Modifier + .fillMaxSize(), + ) { + Tabs(tabs = tabs, pagerState = pagerState) + + TabsContent(tabs = tabs, pagerState = pagerState) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartAddOnItems.kt b/feature/cart/src/main/java/com/niyaj/cart/components/CartAddOnItems.kt similarity index 77% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartAddOnItems.kt rename to feature/cart/src/main/java/com/niyaj/cart/components/CartAddOnItems.kt index 3585639d..fff9e309 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartAddOnItems.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/components/CartAddOnItems.kt @@ -1,5 +1,6 @@ -package com.niyaj.poposroom.features.cart.presentation.components +package com.niyaj.cart.components +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi @@ -8,30 +9,30 @@ 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.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.common.components.StandardFilterChip -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.model.AddOnItem +import com.niyaj.ui.components.StandardFilterChip @OptIn(ExperimentalLayoutApi::class) @Composable fun CartAddOnItems( addOnItems: List = emptyList(), selectedAddOnItem: List = emptyList(), - onClick: (Int) -> Unit = {} + onClick: (Int) -> Unit = {}, ) { Column( modifier = Modifier .fillMaxWidth() - .padding(SpaceSmall), + .background(MaterialTheme.colorScheme.background), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { FlowRow { - for (addOnItem in addOnItems){ + for (addOnItem in addOnItems) { StandardFilterChip( modifier = Modifier .padding(SpaceMini), diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartFooterPlaceOrder.kt b/feature/cart/src/main/java/com/niyaj/cart/components/CartFooterPlaceOrder.kt similarity index 88% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartFooterPlaceOrder.kt rename to feature/cart/src/main/java/com/niyaj/cart/components/CartFooterPlaceOrder.kt index 79f1c9f2..253f9d54 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartFooterPlaceOrder.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/components/CartFooterPlaceOrder.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart.presentation.components +package com.niyaj.cart.components import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource @@ -21,6 +21,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface 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.SpanStyle @@ -28,22 +29,23 @@ import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.withStyle import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall +import com.niyaj.designsystem.theme.LightColor10 +import com.niyaj.designsystem.theme.SpaceSmall @Composable fun CartFooterPlaceOrder( - modifier : Modifier = Modifier, - countTotalItems : Int, - countSelectedItem : Int, - showPrintBtn : Boolean = true, - onClickSelectAll : () -> Unit = {}, - onClickPlaceAllOrder : () -> Unit = {}, - onClickPrintAllOrder : () -> Unit = {}, + modifier: Modifier = Modifier, + countTotalItems: Int, + countSelectedItem: Int, + showPrintBtn: Boolean = true, + onClickSelectAll: () -> Unit = {}, + onClickPlaceAllOrder: () -> Unit = {}, + onClickPrintAllOrder: () -> Unit = {}, ) { Surface( modifier = modifier .fillMaxWidth(), - color = MaterialTheme.colorScheme.surfaceVariant, + color = LightColor10, ) { Row( modifier = Modifier @@ -62,7 +64,7 @@ fun CartFooterPlaceOrder( imageVector = if (countTotalItems == countSelectedItem) Icons.Default.CheckCircle else Icons.Default.CheckCircleOutline, - contentDescription = null, + contentDescription = "Select All Order", tint = if (countTotalItems == countSelectedItem) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary ) @@ -85,7 +87,7 @@ fun CartFooterPlaceOrder( }, style = MaterialTheme.typography.labelSmall, modifier = Modifier.clickable( - interactionSource = MutableInteractionSource(), + interactionSource = remember { MutableInteractionSource() }, indication = null, onClick = onClickSelectAll ) diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItemOrderDetailsSection.kt b/feature/cart/src/main/java/com/niyaj/cart/components/CartItemOrderDetailsSection.kt similarity index 88% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItemOrderDetailsSection.kt rename to feature/cart/src/main/java/com/niyaj/cart/components/CartItemOrderDetailsSection.kt index 650eba41..35a9b7c1 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItemOrderDetailsSection.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/components/CartItemOrderDetailsSection.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart.presentation.components +package com.niyaj.cart.components import androidx.compose.animation.Crossfade import androidx.compose.foundation.background @@ -26,16 +26,16 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.components.TextWithIcon -import com.niyaj.poposroom.features.common.ui.theme.LightColor8 -import com.niyaj.poposroom.features.common.ui.theme.Pewter -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall +import com.niyaj.designsystem.theme.LightColor8 +import com.niyaj.designsystem.theme.Pewter +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.OrderType +import com.niyaj.ui.components.TextWithIcon @Composable fun CartItemOrderDetailsSection( - modifier : Modifier = Modifier, + modifier: Modifier = Modifier, orderId: String = "", customerPhone: String? = "", orderType: OrderType = OrderType.DineIn, @@ -49,7 +49,7 @@ fun CartItemOrderDetailsSection( val containerColor = if (orderType == OrderType.DineIn) Pewter else LightColor8 - val iconColor = if(orderType == OrderType.DineIn) + val iconColor = if (orderType == OrderType.DineIn) MaterialTheme.colorScheme.onPrimaryContainer else MaterialTheme.colorScheme.onSecondaryContainer key(orderId) { @@ -100,7 +100,7 @@ fun CartItemOrderDetailsSection( @Composable fun CartOrderDetailsButtons( - modifier : Modifier = Modifier, + modifier: Modifier = Modifier, selected: Boolean, iconColor: Color, onClick: () -> Unit, diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItemProductDetailsSection.kt b/feature/cart/src/main/java/com/niyaj/cart/components/CartItemProductDetailsSection.kt similarity index 91% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItemProductDetailsSection.kt rename to feature/cart/src/main/java/com/niyaj/cart/components/CartItemProductDetailsSection.kt index c7d25037..306b0cd6 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItemProductDetailsSection.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/components/CartItemProductDetailsSection.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart.presentation.components +package com.niyaj.cart.components import androidx.compose.animation.Crossfade import androidx.compose.foundation.background @@ -31,11 +31,10 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.cart.domain.model.CartProductItem -import com.niyaj.poposroom.features.common.ui.theme.LightColor10 -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.toRupee +import com.niyaj.common.utils.toRupee +import com.niyaj.designsystem.theme.LightColor10 +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.model.CartProductItem @Composable fun CartItemProductDetailsSection( @@ -46,7 +45,6 @@ fun CartItemProductDetailsSection( Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = SpaceSmall) .background(MaterialTheme.colorScheme.surface, RoundedCornerShape(4.dp)) ) { cartProducts.forEach { cartProduct -> @@ -128,7 +126,10 @@ fun CartProduct( ) } - Crossfade(targetState = cartProduct.productQuantity, label = "Product quantity") { + Crossfade( + targetState = cartProduct.productQuantity, + label = "Product quantity" + ) { Text( text = it.toString(), fontWeight = FontWeight.SemiBold, diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItemTotalPriceSection.kt b/feature/cart/src/main/java/com/niyaj/cart/components/CartItemTotalPriceSection.kt similarity index 93% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItemTotalPriceSection.kt rename to feature/cart/src/main/java/com/niyaj/cart/components/CartItemTotalPriceSection.kt index 799db438..e4595440 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItemTotalPriceSection.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/components/CartItemTotalPriceSection.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart.presentation.components +package com.niyaj.cart.components import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background @@ -26,9 +26,9 @@ 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.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.ui.theme.LightColor8 -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall +import com.niyaj.designsystem.theme.LightColor8 +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.OrderType @Composable fun CartItemTotalPriceSection( diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItems.kt b/feature/cart/src/main/java/com/niyaj/cart/components/CartItems.kt similarity index 50% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItems.kt rename to feature/cart/src/main/java/com/niyaj/cart/components/CartItems.kt index 748ca2db..5f3ba91d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartItems.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/components/CartItems.kt @@ -1,10 +1,6 @@ -package com.niyaj.poposroom.features.cart.presentation.components +package com.niyaj.cart.components -import androidx.compose.foundation.background -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.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -13,19 +9,19 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Card -import androidx.compose.material.MaterialTheme +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.cart.domain.model.CartItem -import com.niyaj.poposroom.features.cart.domain.model.OrderPrice -import com.niyaj.poposroom.features.common.ui.theme.ProfilePictureSizeSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall +import com.niyaj.designsystem.theme.ProfilePictureSizeSmall +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.AddOnItem +import com.niyaj.model.CartItem +import com.niyaj.model.OrderPrice @Composable @@ -57,8 +53,8 @@ fun CartItems( key = { _, cartItem -> cartItem.orderId } - ){ index, cartItem -> - if(cartItem.cartProducts.isNotEmpty()) { + ) { index, cartItem -> + if (cartItem.cartProducts.isNotEmpty()) { CartItem( cartItem = cartItem, doesSelected = selectedCartItems.contains(cartItem.orderId), @@ -85,6 +81,7 @@ fun CartItems( } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun CartItem( cartItem: CartItem, @@ -98,93 +95,77 @@ fun CartItem( onClickIncreaseQty: (orderId: Int, productId: Int) -> Unit, onClickAddOnItem: (addOnItemId: Int, orderId: Int) -> Unit, onClickPlaceOrder: (orderId: Int) -> Unit, - onClickPrintOrder: (orderId: Int) -> Unit = {} + onClickPrintOrder: (orderId: Int) -> Unit = {}, ) { - val newOrderId = if(!cartItem.customerAddress.isNullOrEmpty()){ - cartItem.customerAddress.uppercase().plus(" -") + val newOrderId = if (!cartItem.customerAddress.isNullOrEmpty()) { + cartItem.customerAddress!!.uppercase().plus(" -") .plus(cartItem.orderId) - }else{ + } else { cartItem.orderId.toString() } val orderPrice = cartItem.orderPrice.collectAsStateWithLifecycle(OrderPrice()).value Card( modifier = Modifier - .fillMaxWidth() - .background( - MaterialTheme.colors.surface, - RoundedCornerShape(6.dp) - ) - .clickable( - indication = null, - interactionSource = remember { - MutableInteractionSource() - } - ) { + .fillMaxWidth(), + elevation = CardDefaults.cardElevation(defaultElevation = 6.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.background + ), + shape = RoundedCornerShape(6.dp), + onClick = { + onSelectCartOrder(cartItem.orderId) + } + ) { + CartItemOrderDetailsSection( + orderId = newOrderId, + orderType = cartItem.orderType, + customerPhone = cartItem.customerPhone, + selected = doesSelected, + onClick = { onSelectCartOrder(cartItem.orderId) }, - elevation = 6.dp - ) { - Column( - modifier = Modifier - .fillMaxWidth(), - ) { - CartItemOrderDetailsSection( - orderId = newOrderId, - orderType = cartItem.orderType, - customerPhone = cartItem.customerPhone, - selected = doesSelected, - onClick = { - onSelectCartOrder(cartItem.orderId) - }, - onEditClick = { - onClickEditOrder(cartItem.orderId) - }, - onViewClick = { - onClickViewOrder(cartItem.orderId) - } - ) - - Spacer(modifier = Modifier.height(SpaceMini)) - - CartItemProductDetailsSection( - cartProducts = cartItem.cartProducts, - decreaseQuantity = { - onClickDecreaseQty(cartItem.orderId, it) - }, - increaseQuantity = { - onClickIncreaseQty(cartItem.orderId, it) - } - ) - - - if(addOnItems.isNotEmpty()){ - Spacer(modifier = Modifier.height(SpaceSmall)) + onEditClick = { + onClickEditOrder(cartItem.orderId) + }, + onViewClick = { + onClickViewOrder(cartItem.orderId) + } + ) - CartAddOnItems( - addOnItems = addOnItems, - selectedAddOnItem = cartItem.addOnItems.collectAsStateWithLifecycle(initialValue = emptyList()).value, - onClick = { - onClickAddOnItem(it, cartItem.orderId) - }, - ) + CartItemProductDetailsSection( + cartProducts = cartItem.cartProducts, + decreaseQuantity = { + onClickDecreaseQty(cartItem.orderId, it) + }, + increaseQuantity = { + onClickIncreaseQty(cartItem.orderId, it) } + ) - Spacer(modifier = Modifier.height(SpaceSmall)) - CartItemTotalPriceSection( - itemCount = cartItem.cartProducts.size, - orderType = cartItem.orderType, - totalPrice = orderPrice.totalPrice, - discountPrice = orderPrice.discountPrice, - showPrintBtn = showPrintBtn, - onClickPlaceOrder = { - onClickPlaceOrder(cartItem.orderId) + if (addOnItems.isNotEmpty()) { + CartAddOnItems( + addOnItems = addOnItems, + selectedAddOnItem = cartItem.addOnItems.collectAsStateWithLifecycle(initialValue = emptyList()).value, + onClick = { + onClickAddOnItem(it, cartItem.orderId) }, - onClickPrintOrder = { - onClickPrintOrder(cartItem.orderId) - } ) } + + CartItemTotalPriceSection( + itemCount = cartItem.cartProducts.size, + orderType = cartItem.orderType, + totalPrice = orderPrice.totalPrice, + discountPrice = orderPrice.discountPrice, + showPrintBtn = showPrintBtn, + onClickPlaceOrder = { + onClickPlaceOrder(cartItem.orderId) + }, + onClickPrintOrder = { + onClickPrintOrder(cartItem.orderId) + } + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartTabItem.kt b/feature/cart/src/main/java/com/niyaj/cart/components/CartTabItem.kt similarity index 84% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartTabItem.kt rename to feature/cart/src/main/java/com/niyaj/cart/components/CartTabItem.kt index 91b5f893..8a1b1515 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/components/CartTabItem.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/components/CartTabItem.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart.presentation.components +package com.niyaj.cart.components import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.DeliveryDining @@ -11,12 +11,12 @@ typealias ComposableFunction = @Composable () -> Unit sealed class CartTabItem( val icon: ImageVector, val title: String, - val screen: ComposableFunction -){ + val screen: ComposableFunction, +) { data class DineInItem( val content: @Composable () -> Unit = {}, - ): CartTabItem( + ) : CartTabItem( icon = Icons.Default.Dining, title = "DineIn", screen = { @@ -26,7 +26,7 @@ sealed class CartTabItem( data class DineOutItem( val content: @Composable () -> Unit = {}, - ): CartTabItem( + ) : CartTabItem( icon = Icons.Default.DeliveryDining, title = "DineOut", screen = { diff --git a/app/src/main/java/com/niyaj/poposroom/features/common/components/Tabs.kt b/feature/cart/src/main/java/com/niyaj/cart/components/Tabs.kt similarity index 92% rename from app/src/main/java/com/niyaj/poposroom/features/common/components/Tabs.kt rename to feature/cart/src/main/java/com/niyaj/cart/components/Tabs.kt index e0166c78..68e2d806 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/common/components/Tabs.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/components/Tabs.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.common.components +package com.niyaj.cart.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.fillMaxWidth @@ -16,8 +16,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.cart.presentation.components.CartTabItem -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceMini import kotlinx.coroutines.launch @OptIn(ExperimentalFoundationApi::class) diff --git a/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInEvent.kt b/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInEvent.kt new file mode 100644 index 00000000..56d91949 --- /dev/null +++ b/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInEvent.kt @@ -0,0 +1,20 @@ +package com.niyaj.cart.dine_in + +sealed class DineInEvent { + + data class SelectDineInOrder(val orderId: Int) : DineInEvent() + + data object SelectAllDineInOrder : DineInEvent() + + data class IncreaseQuantity(val orderId: Int, val productId: Int) : DineInEvent() + + data class DecreaseQuantity(val orderId: Int, val productId: Int) : DineInEvent() + + data class UpdateAddOnItemInCart(val itemId: Int, val orderId: Int) : DineInEvent() + + data class PlaceDineInOrder(val orderId: Int) : DineInEvent() + + data object PlaceAllDineInOrder : DineInEvent() + + data object RefreshDineInOrder : DineInEvent() +} diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInScreen.kt b/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInScreen.kt similarity index 79% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInScreen.kt rename to feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInScreen.kt index 916056b8..16150a3f 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInScreen.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart.presentation.dine_in +package com.niyaj.cart.dine_in import android.annotation.SuppressLint import androidx.compose.animation.AnimatedVisibility @@ -19,24 +19,23 @@ import androidx.compose.ui.res.painterResource import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.R -import com.niyaj.poposroom.features.cart.presentation.components.CartFooterPlaceOrder -import com.niyaj.poposroom.features.cart.presentation.components.CartItems -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrollingUp -import com.niyaj.poposroom.features.destinations.AddEditCartOrderScreenDestination -import com.niyaj.poposroom.features.destinations.MainFeedScreenDestination -import com.niyaj.poposroom.features.destinations.OrderDetailsScreenDestination -import com.niyaj.poposroom.features.destinations.OrderScreenDestination -import com.ramcosta.composedestinations.navigation.navigate +import com.niyaj.cart.components.CartFooterPlaceOrder +import com.niyaj.cart.components.CartItems +import com.niyaj.core.ui.R +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrollingUp import kotlinx.coroutines.flow.collectLatest @Composable @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") fun DineInScreen( navController: NavController, + onClickEditOrder: (Int) -> Unit, + onClickOrderDetails: (Int) -> Unit, + onNavigateToOrderScreen: () -> Unit, viewModel: DineInViewModel = hiltViewModel(), ) { val snackbarHostState = remember { SnackbarHostState() } @@ -61,7 +60,7 @@ fun DineInScreen( duration = SnackbarDuration.Short ) if (result == SnackbarResult.ActionPerformed) { - navController.navigate(OrderScreenDestination()) + onNavigateToOrderScreen() } } @@ -71,8 +70,6 @@ fun DineInScreen( duration = SnackbarDuration.Short ) } - - is UiEvent.IsLoading -> {} } } } @@ -117,7 +114,7 @@ fun DineInScreen( buttonText = "Add Item To Cart", image = painterResource(R.drawable.emptycart), onClick = { - navController.navigate(MainFeedScreenDestination()) + navController.navigate(Screens.HomeScreen) } ) } else { @@ -129,12 +126,8 @@ fun DineInScreen( onSelectCartOrder = { viewModel.onEvent(DineInEvent.SelectDineInOrder(it)) }, - onClickEditOrder = { - navController.navigate(AddEditCartOrderScreenDestination(it)) - }, - onClickViewOrder = { - navController.navigate(OrderDetailsScreenDestination(it)) - }, + onClickEditOrder = onClickEditOrder, + onClickViewOrder = onClickOrderDetails, onClickDecreaseQty = { cartOrderId, productId -> viewModel.onEvent(DineInEvent.DecreaseQuantity(cartOrderId, productId)) }, diff --git a/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInState.kt b/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInState.kt new file mode 100644 index 00000000..b88a1a10 --- /dev/null +++ b/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInState.kt @@ -0,0 +1,8 @@ +package com.niyaj.cart.dine_in + +import com.niyaj.model.CartItem + +data class DineInState( + val isLoading: Boolean = true, + val items: List = emptyList(), +) diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInViewModel.kt b/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInViewModel.kt similarity index 93% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInViewModel.kt rename to feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInViewModel.kt index e8e09680..53ded2e0 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_in/DineInViewModel.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/dine_in/DineInViewModel.kt @@ -1,10 +1,10 @@ -package com.niyaj.poposroom.features.cart.presentation.dine_in +package com.niyaj.cart.dine_in import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.cart.domain.repository.CartRepository -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.CartRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutEvent.kt b/feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutEvent.kt similarity index 67% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutEvent.kt rename to feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutEvent.kt index adf94c84..f83f9e86 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutEvent.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutEvent.kt @@ -1,10 +1,10 @@ -package com.niyaj.poposroom.features.cart.presentation.dine_out +package com.niyaj.cart.dine_out sealed class DineOutEvent { data class SelectDineOutOrder(val orderId: Int): DineOutEvent() - object SelectAllDineOutOrder: DineOutEvent() + data object SelectAllDineOutOrder: DineOutEvent() data class IncreaseQuantity(val orderId: Int, val productId: Int): DineOutEvent() @@ -14,7 +14,7 @@ sealed class DineOutEvent { data class PlaceDineOutOrder(val orderId: Int): DineOutEvent() - object PlaceAllDineOutOrder: DineOutEvent() + data object PlaceAllDineOutOrder: DineOutEvent() - object RefreshDineOutOrder: DineOutEvent() + data object RefreshDineOutOrder: DineOutEvent() } diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutScreen.kt b/feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutScreen.kt similarity index 84% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutScreen.kt rename to feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutScreen.kt index fdfce185..cefae6c9 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutScreen.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutScreen.kt @@ -1,8 +1,7 @@ -package com.niyaj.poposroom.features.cart.presentation.dine_out +package com.niyaj.cart.dine_out import android.Manifest import android.annotation.SuppressLint -import android.app.Activity import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothManager import android.content.Intent @@ -30,26 +29,24 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberMultiplePermissionsState -import com.niyaj.poposroom.R -import com.niyaj.poposroom.features.cart.presentation.components.CartFooterPlaceOrder -import com.niyaj.poposroom.features.cart.presentation.components.CartItems -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrollingUp -import com.niyaj.poposroom.features.destinations.AddEditCartOrderScreenDestination -import com.niyaj.poposroom.features.destinations.MainFeedScreenDestination -import com.niyaj.poposroom.features.destinations.OrderDetailsScreenDestination -import com.niyaj.poposroom.features.destinations.OrderScreenDestination -import com.ramcosta.composedestinations.navigation.navigate +import com.niyaj.cart.components.CartFooterPlaceOrder +import com.niyaj.cart.components.CartItems +import com.niyaj.core.ui.R +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrollingUp import kotlinx.coroutines.flow.collectLatest -import timber.log.Timber @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @OptIn(ExperimentalPermissionsApi::class) @Composable fun DineOutScreen( navController: NavController, + onClickEditOrder: (Int) -> Unit, + onClickOrderDetails: (Int) -> Unit, + onNavigateToOrderScreen: () -> Unit, viewModel: DineOutViewModel = hiltViewModel(), ) { val snackbarHostState = remember { SnackbarHostState() } @@ -78,13 +75,7 @@ fun DineOutScreen( val enableBluetoothContract = rememberLauncherForActivityResult( ActivityResultContracts.StartActivityForResult() - ) { - if (it.resultCode == Activity.RESULT_OK) { - Timber.d("bluetoothLauncher", "Success") - } else { - Timber.w("bluetoothLauncher", "Failed") - } - } + ) {} // This intent will open the enable bluetooth dialog val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) @@ -100,6 +91,7 @@ fun DineOutScreen( val printOrder: (Int) -> Unit = { if (bluetoothPermissions.allPermissionsGranted) { if (bluetoothAdapter?.isEnabled == true) { +// Todo: use print view model to print data // Bluetooth is on print the receipt // printViewModel.onPrintEvent(PrintEvent.PrintOrder(it)) } else { @@ -148,7 +140,7 @@ fun DineOutScreen( duration = SnackbarDuration.Short ) if (result == SnackbarResult.ActionPerformed) { - navController.navigate(OrderScreenDestination()) + onNavigateToOrderScreen() } } @@ -158,8 +150,6 @@ fun DineOutScreen( duration = SnackbarDuration.Short ) } - - is UiEvent.IsLoading -> {} } } } @@ -207,7 +197,7 @@ fun DineOutScreen( buttonText = "Add Item To Cart", image = painterResource(R.drawable.emptycarttwo), onClick = { - navController.navigate(MainFeedScreenDestination()) + navController.navigate(Screens.HomeScreen) } ) } else { @@ -220,12 +210,8 @@ fun DineOutScreen( onSelectCartOrder = { viewModel.onEvent(DineOutEvent.SelectDineOutOrder(it)) }, - onClickEditOrder = { - navController.navigate(AddEditCartOrderScreenDestination(it)) - }, - onClickViewOrder = { - navController.navigate(OrderDetailsScreenDestination(it)) - }, + onClickEditOrder = onClickEditOrder, + onClickViewOrder = onClickOrderDetails, onClickDecreaseQty = { cartOrderId, productId -> viewModel.onEvent( DineOutEvent.DecreaseQuantity(cartOrderId, productId) diff --git a/feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutState.kt b/feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutState.kt new file mode 100644 index 00000000..1e91fbd0 --- /dev/null +++ b/feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutState.kt @@ -0,0 +1,8 @@ +package com.niyaj.cart.dine_out + +import com.niyaj.model.CartItem + +data class DineOutState( + val isLoading: Boolean = true, + val items: List = emptyList(), +) diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutViewModel.kt b/feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutViewModel.kt similarity index 93% rename from app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutViewModel.kt rename to feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutViewModel.kt index cac41b10..a0a2f7ef 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart/presentation/dine_out/DineOutViewModel.kt +++ b/feature/cart/src/main/java/com/niyaj/cart/dine_out/DineOutViewModel.kt @@ -1,10 +1,10 @@ -package com.niyaj.poposroom.features.cart.presentation.dine_out +package com.niyaj.cart.dine_out import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.cart.domain.repository.CartRepository -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.CartRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted diff --git a/feature/cart/src/test/java/com/niyaj/cart/ExampleUnitTest.kt b/feature/cart/src/test/java/com/niyaj/cart/ExampleUnitTest.kt new file mode 100644 index 00000000..306e00eb --- /dev/null +++ b/feature/cart/src/test/java/com/niyaj/cart/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.cart + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/cart_selected/.gitignore b/feature/cart_selected/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/cart_selected/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/cart_selected/build.gradle.kts b/feature/cart_selected/build.gradle.kts new file mode 100644 index 00000000..88ba1abc --- /dev/null +++ b/feature/cart_selected/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.cart_selected" + + ksp { + arg("compose-destinations.moduleName", "cart_selected") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/cart_selected/proguard-rules.pro b/feature/cart_selected/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/cart_selected/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/cart_selected/src/androidTest/java/com/niyaj/cart_selected/ExampleInstrumentedTest.kt b/feature/cart_selected/src/androidTest/java/com/niyaj/cart_selected/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..35042271 --- /dev/null +++ b/feature/cart_selected/src/androidTest/java/com/niyaj/cart_selected/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.cart_selected + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.cart_selected", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/cart_selected/src/main/AndroidManifest.xml b/feature/cart_selected/src/main/AndroidManifest.xml new file mode 100644 index 00000000..44008a43 --- /dev/null +++ b/feature/cart_selected/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/selected/presentation/SelectOrderScreen.kt b/feature/cart_selected/src/main/java/com/niyaj/cart_selected/SelectOrderScreen.kt similarity index 84% rename from app/src/main/java/com/niyaj/poposroom/features/selected/presentation/SelectOrderScreen.kt rename to feature/cart_selected/src/main/java/com/niyaj/cart_selected/SelectOrderScreen.kt index a86d5575..0b1ccc31 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/selected/presentation/SelectOrderScreen.kt +++ b/feature/cart_selected/src/main/java/com/niyaj/cart_selected/SelectOrderScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.selected.presentation +package com.niyaj.cart_selected import androidx.compose.animation.Crossfade import androidx.compose.foundation.background @@ -49,37 +49,41 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrder -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardScaffoldWithOutDrawer -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceMedium -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.common.utils.toTimeSpan -import com.niyaj.poposroom.features.destinations.AddEditCartOrderScreenDestination -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags -import com.niyaj.poposroom.features.selected.domain.utils.SelectedTestTag.SELECTED_SCREEN_NOTE -import com.niyaj.poposroom.features.selected.domain.utils.SelectedTestTag.SELECTED_SCREEN_TITLE +import com.niyaj.common.utils.toTimeSpan +import com.niyaj.data.utils.CartOrderTestTags +import com.niyaj.data.utils.ProductTestTags +import com.niyaj.data.utils.SelectedTestTag.SELECTED_SCREEN_NOTE +import com.niyaj.data.utils.SelectedTestTag.SELECTED_SCREEN_TITLE +import com.niyaj.designsystem.theme.SpaceMedium +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.model.CartOrder +import com.niyaj.model.OrderType +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.navigation.navigate +import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.result.ResultBackNavigator import kotlinx.coroutines.flow.collectLatest -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.SelectOrderScreen +) @Composable fun SelectOrderScreen( navController: NavController, + onEditClick: (Int) -> Unit, + resultBackNavigator: ResultBackNavigator, viewModel: SelectedViewModel = hiltViewModel(), - resultBackNavigator: ResultBackNavigator ) { val lazyListState = rememberLazyListState() @@ -94,11 +98,11 @@ fun SelectOrderScreen( LaunchedEffect(key1 = true) { viewModel.eventFlow.collectLatest { data -> - when(data) { - is UiEvent.IsLoading -> {} + when (data) { is UiEvent.OnError -> { resultBackNavigator.navigateBack(data.errorMessage) } + is UiEvent.OnSuccess -> { resultBackNavigator.navigateBack(data.successMessage) } @@ -106,13 +110,12 @@ fun SelectOrderScreen( } } - StandardScaffoldWithOutDrawer( title = SELECTED_SCREEN_TITLE, onBackClick = { navController.navigateUp() }, - showBottomBar = !lazyListState.isScrolled, + showBottomBar = !lazyListState.isScrolled && selectedOrder != 0, bottomBar = { StandardButton( modifier = Modifier @@ -123,7 +126,7 @@ fun SelectOrderScreen( text = CartOrderTestTags.CREATE_NEW_CART_ORDER, icon = Icons.Default.Add, onClick = { - navController.navigate(AddEditCartOrderScreenDestination()) + navController.navigate(Screens.AddEditCartOrderScreen) } ) } @@ -139,10 +142,11 @@ fun SelectOrderScreen( text = CartOrderTestTags.CART_ORDER_NOT_AVAIlABLE, buttonText = CartOrderTestTags.CREATE_NEW_CART_ORDER, onClick = { - navController.navigate(AddEditCartOrderScreenDestination()) + navController.navigate(Screens.AddEditCartOrderScreen) } ) } + is UiState.Success -> { LazyColumn( modifier = Modifier @@ -150,7 +154,7 @@ fun SelectOrderScreen( .padding(SpaceMedium), state = lazyListState, ) { - item("ExistError") { + item("Note") { Spacer(modifier = Modifier.height(SpaceSmall)) ListItem( @@ -194,9 +198,7 @@ fun SelectOrderScreen( selectedId = it openDialog.value = true }, - onEditClick = { - navController.navigate(AddEditCartOrderScreenDestination(it)) - } + onEditClick = onEditClick, ) Spacer(modifier = Modifier.height(SpaceMedium)) diff --git a/app/src/main/java/com/niyaj/poposroom/features/selected/presentation/SelectedViewModel.kt b/feature/cart_selected/src/main/java/com/niyaj/cart_selected/SelectedViewModel.kt similarity index 84% rename from app/src/main/java/com/niyaj/poposroom/features/selected/presentation/SelectedViewModel.kt rename to feature/cart_selected/src/main/java/com/niyaj/cart_selected/SelectedViewModel.kt index 64d68ba0..25b92299 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/selected/presentation/SelectedViewModel.kt +++ b/feature/cart_selected/src/main/java/com/niyaj/cart_selected/SelectedViewModel.kt @@ -1,13 +1,14 @@ -package com.niyaj.poposroom.features.selected.presentation +package com.niyaj.cart_selected import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrder -import com.niyaj.poposroom.features.cart_order.domain.repository.CartOrderRepository -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.selected.domain.model.Selected +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.CartOrderRepository +import com.niyaj.model.CartOrder +import com.niyaj.model.SELECTED_ID +import com.niyaj.model.Selected +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow @@ -64,7 +65,10 @@ class SelectedViewModel @Inject constructor( fun selectCartOrder(orderId: Int) { viewModelScope.launch { val result = cartOrderRepository.insertOrUpdateSelectedOrder( - Selected(orderId = orderId) + Selected( + selectedId = SELECTED_ID, + orderId = orderId + ) ) when (result) { diff --git a/feature/cart_selected/src/test/java/com/niyaj/cart_selected/ExampleUnitTest.kt b/feature/cart_selected/src/test/java/com/niyaj/cart_selected/ExampleUnitTest.kt new file mode 100644 index 00000000..1248cb59 --- /dev/null +++ b/feature/cart_selected/src/test/java/com/niyaj/cart_selected/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.cart_selected + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/cartorder/.gitignore b/feature/cartorder/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/cartorder/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/cartorder/build.gradle.kts b/feature/cartorder/build.gradle.kts new file mode 100644 index 00000000..cd656f6d --- /dev/null +++ b/feature/cartorder/build.gradle.kts @@ -0,0 +1,31 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.cartorder" + + ksp { + arg("compose-destinations.moduleName", "cartorder") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.permissions) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/cartorder/proguard-rules.pro b/feature/cartorder/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/cartorder/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/cartorder/src/androidTest/java/com/niyaj/cartorder/ExampleInstrumentedTest.kt b/feature/cartorder/src/androidTest/java/com/niyaj/cartorder/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..88a80c9c --- /dev/null +++ b/feature/cartorder/src/androidTest/java/com/niyaj/cartorder/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.cartorder + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.cartorder", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/cartorder/src/main/AndroidManifest.xml b/feature/cartorder/src/main/AndroidManifest.xml new file mode 100644 index 00000000..de87c72a --- /dev/null +++ b/feature/cartorder/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/CartOrderScreen.kt b/feature/cartorder/src/main/java/com/niyaj/cartorder/CartOrderScreen.kt similarity index 83% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/CartOrderScreen.kt rename to feature/cartorder/src/main/java/com/niyaj/cartorder/CartOrderScreen.kt index 41b0e3be..25cd2785 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/CartOrderScreen.kt +++ b/feature/cartorder/src/main/java/com/niyaj/cartorder/CartOrderScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart_order.presentation +package com.niyaj.cartorder import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility @@ -20,10 +20,6 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.rememberLazyGridState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.DropdownMenu -import androidx.compose.material.DropdownMenuItem -import androidx.compose.material.Icon -import androidx.compose.material.IconButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.OpenInNew @@ -32,8 +28,12 @@ import androidx.compose.material.icons.filled.TaskAlt import androidx.compose.material.icons.filled.Visibility import androidx.compose.material3.AlertDialog import androidx.compose.material3.CardDefaults +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ElevatedCard import androidx.compose.material3.FabPosition +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text @@ -58,39 +58,45 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrder -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CART_ORDER_ITEM_TAG -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CART_ORDER_NOT_AVAIlABLE -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CART_ORDER_SCREEN_TITLE -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CART_ORDER_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CREATE_NEW_CART_ORDER -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.DELETE_CART_ORDER_ITEM_MESSAGE -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.DELETE_CART_ORDER_ITEM_TITLE -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.ScaffoldNavActions -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.destinations.AddEditCartOrderScreenDestination +import com.niyaj.cartorder.destinations.AddEditCartOrderScreenDestination +import com.niyaj.data.utils.CartOrderTestTags.CART_ORDER_ITEM_TAG +import com.niyaj.data.utils.CartOrderTestTags.CART_ORDER_NOT_AVAIlABLE +import com.niyaj.data.utils.CartOrderTestTags.CART_ORDER_SCREEN_TITLE +import com.niyaj.data.utils.CartOrderTestTags.CART_ORDER_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.CartOrderTestTags.CREATE_NEW_CART_ORDER +import com.niyaj.data.utils.CartOrderTestTags.DELETE_CART_ORDER_ITEM_MESSAGE +import com.niyaj.data.utils.CartOrderTestTags.DELETE_CART_ORDER_ITEM_TITLE +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.CartOrder +import com.niyaj.model.OrderType +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.navigate import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.CartOrderScreen +) @Composable fun CartOrderScreen( navController: NavController, + onClickOrderDetails: (Int) -> Unit, + resultRecipient: ResultRecipient, viewModel: CartOrderViewModel = hiltViewModel(), - resultRecipient: ResultRecipient ) { val scope = rememberCoroutineScope() val snackbarState = remember { SnackbarHostState() } @@ -113,8 +119,7 @@ fun CartOrderScreen( LaunchedEffect(key1 = true) { viewModel.eventFlow.collectLatest { event -> - when(event) { - is UiEvent.IsLoading -> {} + when (event) { is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(event.errorMessage) @@ -122,6 +127,7 @@ fun CartOrderScreen( viewModel.deselectItems() } + is UiEvent.OnSuccess -> { scope.launch { snackbarState.showSnackbar(event.successMessage) @@ -142,10 +148,11 @@ fun CartOrderScreen( } resultRecipient.onNavResult { result -> - when(result) { + when (result) { is NavResult.Canceled -> { viewModel.deselectItems() } + is NavResult.Value -> { scope.launch { viewModel.deselectItems() @@ -157,10 +164,7 @@ fun CartOrderScreen( StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) CART_ORDER_SCREEN_TITLE else "${selectedItems.size} Selected", - selectionCount = selectedItems.size, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyGridState.isScrolled, @@ -176,7 +180,6 @@ fun CartOrderScreen( } ) }, - fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, navActions = { CartOrderScaffoldNavActions( selectionCount = selectedItems.size, @@ -191,9 +194,10 @@ fun CartOrderScreen( navController.navigate(AddEditCartOrderScreenDestination(selectedItems.first())) }, onToggleMenu = { showMenu = !showMenu }, - onDismissDropdown = { showMenu = false }, + onDismissDropdown = { showMenu = false }, onDropItemClick = { showMenu = false + viewModel.onClickViewAllOrder() }, onSearchTextChanged = viewModel::searchTextChanged, onClearClick = viewModel::clearSearchText, @@ -201,18 +205,17 @@ fun CartOrderScreen( onSelectAllClick = viewModel::selectAllItems, onSelectOrderClick = viewModel::selectCartOrder, onSettingsClick = { /*TODO*/ }, - onClickViewDetails = { /*TODO*/ } + onClickViewDetails = { + onClickOrderDetails(selectedItems.first()) + } ) }, - onEditClick = { - navController.navigate(AddEditCartOrderScreenDestination(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true - }, + fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, onBackClick = viewModel::closeSearchBar, + snackbarHostState = snackbarState, ) { _ -> when (state) { is UiState.Loading -> LoadingIndicator() @@ -225,6 +228,7 @@ fun CartOrderScreen( } ) } + is UiState.Success -> { LazyVerticalGrid( modifier = Modifier @@ -249,6 +253,8 @@ fun CartOrderScreen( onClick = { if (selectedItems.isNotEmpty()) { viewModel.selectItem(it) + }else { + onClickOrderDetails(it) } }, onLongClick = viewModel::selectItem @@ -307,18 +313,18 @@ fun CartOrderScreen( fun CartOrderScaffoldNavActions( selectionCount: Int, showSearchIcon: Boolean, - showSearchBar : Boolean, - searchText : String, + showSearchBar: Boolean, + searchText: String, showMenu: Boolean, - onDeleteClick : () -> Unit, - onEditClick : () -> Unit, + onDeleteClick: () -> Unit, + onEditClick: () -> Unit, onToggleMenu: () -> Unit, onDismissDropdown: () -> Unit, - onDropItemClick : () -> Unit, - onSearchTextChanged : (String) -> Unit, - onClearClick : () -> Unit, - onSearchClick : () -> Unit, - onSettingsClick : () -> Unit, + onDropItemClick: () -> Unit, + onSearchTextChanged: (String) -> Unit, + onClearClick: () -> Unit, + onSearchClick: () -> Unit, + onSettingsClick: () -> Unit, onSelectOrderClick: () -> Unit, onClickViewDetails: () -> Unit, onSelectAllClick: () -> Unit, @@ -353,22 +359,20 @@ fun CartOrderScaffoldNavActions( onDismissRequest = onDismissDropdown, ) { DropdownMenuItem( - onClick = onDropItemClick - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - ) { - Icon( - imageVector = Icons.Default.Visibility, - contentDescription = "View All", - ) - Spacer(modifier = Modifier.width(SpaceSmall)) + onClick = onDropItemClick, + text = { Text( text = "View All", style = MaterialTheme.typography.labelSmall ) + }, + trailingIcon = { + Icon( + imageVector = Icons.Default.Visibility, + contentDescription = "View All", + ) } - } + ) } } }, diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/CartOrderViewModel.kt b/feature/cartorder/src/main/java/com/niyaj/cartorder/CartOrderViewModel.kt similarity index 77% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/CartOrderViewModel.kt rename to feature/cartorder/src/main/java/com/niyaj/cartorder/CartOrderViewModel.kt index ced01b8e..fe28e77a 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/CartOrderViewModel.kt +++ b/feature/cartorder/src/main/java/com/niyaj/cartorder/CartOrderViewModel.kt @@ -1,16 +1,18 @@ -package com.niyaj.poposroom.features.cart_order.presentation +package com.niyaj.cartorder import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.cart_order.domain.repository.CartOrderRepository -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.selected.domain.model.Selected +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.CartOrderRepository +import com.niyaj.model.Selected +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.stateIn @@ -19,12 +21,12 @@ import javax.inject.Inject @HiltViewModel class CartOrderViewModel @Inject constructor( - private val cartOrderRepository: CartOrderRepository + private val cartOrderRepository: CartOrderRepository, ) : BaseViewModel() { override var totalItems: List = emptyList() - val text = snapshotFlow { _searchText.value } + private val _viewAll = MutableStateFlow(false) @OptIn(ExperimentalCoroutinesApi::class) val selectedId = cartOrderRepository.getSelectedCartOrder() @@ -39,8 +41,10 @@ class CartOrderViewModel @Inject constructor( @OptIn(ExperimentalCoroutinesApi::class) val cartOrders = snapshotFlow { _searchText.value } - .flatMapLatest { text -> - cartOrderRepository.getAllCartOrders(text).mapLatest { list -> + .combine(_viewAll) { text, viewAll -> + cartOrderRepository.getAllCartOrders(text, viewAll) + }.flatMapLatest { listFlow -> + listFlow.mapLatest { list -> val data = list.sortedByDescending { it.orderId == selectedId.value } totalItems = data.map { it.orderId } @@ -74,6 +78,12 @@ class CartOrderViewModel @Inject constructor( } } + fun onClickViewAllOrder() { + viewModelScope.launch { + _viewAll.value = !_viewAll.value + } + } + override fun deleteItems() { super.deleteItems() diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderEvent.kt b/feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderEvent.kt similarity index 62% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderEvent.kt rename to feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderEvent.kt index 9ffc60d3..7ad20cf8 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderEvent.kt +++ b/feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderEvent.kt @@ -1,8 +1,8 @@ -package com.niyaj.poposroom.features.cart_order.presentation.add_edit +package com.niyaj.cartorder.add_edit -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.customer.domain.model.Customer +import com.niyaj.model.Address +import com.niyaj.model.Customer +import com.niyaj.model.OrderType sealed interface AddEditCartOrderEvent { @@ -16,7 +16,7 @@ sealed interface AddEditCartOrderEvent { data class CustomerChanged(val customer: Customer): AddEditCartOrderEvent - object DoesChargesIncluded: AddEditCartOrderEvent + data object DoesChargesIncluded: AddEditCartOrderEvent data class CreateOrUpdateCartOrder(val cartOrderId: Int = 0): AddEditCartOrderEvent } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderScreen.kt b/feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderScreen.kt similarity index 85% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderScreen.kt rename to feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderScreen.kt index 65ebb68a..005fdd94 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderScreen.kt +++ b/feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart_order.presentation.add_edit +package com.niyaj.cartorder.add_edit import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn @@ -47,38 +47,42 @@ import androidx.compose.ui.window.PopupProperties import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.ADDRESS_NAME_ERROR_FIELD -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.ADDRESS_NAME_FIELD -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.ADD_EDIT_CART_ORDER_SCREEN -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CHARGES_INCLUDED_FIELD -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CREATE_NEW_CART_ORDER -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CUSTOMER_PHONE_ERROR_FIELD -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.CUSTOMER_PHONE_FIELD -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.EDIT_CART_ORDER -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.ORDER_ID_FIELD -import com.niyaj.poposroom.features.cart_order.domain.utils.CartOrderTestTags.ORDER_TYPE_FIELD -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.MultiSelector -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardOutlinedTextField -import com.niyaj.poposroom.features.common.components.StandardScaffoldWithOutDrawer -import com.niyaj.poposroom.features.common.ui.theme.SpaceMedium -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags +import com.niyaj.data.utils.CartOrderTestTags.ADDRESS_NAME_ERROR_FIELD +import com.niyaj.data.utils.CartOrderTestTags.ADDRESS_NAME_FIELD +import com.niyaj.data.utils.CartOrderTestTags.ADD_EDIT_CART_ORDER_SCREEN +import com.niyaj.data.utils.CartOrderTestTags.CHARGES_INCLUDED_FIELD +import com.niyaj.data.utils.CartOrderTestTags.CREATE_NEW_CART_ORDER +import com.niyaj.data.utils.CartOrderTestTags.CUSTOMER_PHONE_ERROR_FIELD +import com.niyaj.data.utils.CartOrderTestTags.CUSTOMER_PHONE_FIELD +import com.niyaj.data.utils.CartOrderTestTags.EDIT_CART_ORDER +import com.niyaj.data.utils.CartOrderTestTags.ORDER_ID_FIELD +import com.niyaj.data.utils.CartOrderTestTags.ORDER_TYPE_FIELD +import com.niyaj.data.utils.ProductTestTags +import com.niyaj.designsystem.theme.SpaceMedium +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.model.OrderType +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.MultiSelector +import com.niyaj.ui.components.PhoneNoCountBox +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.result.ResultBackNavigator @OptIn(ExperimentalMaterial3Api::class) -@Destination +@Destination( + route = Screens.AddEditCartOrderScreen +) @Composable fun AddEditCartOrderScreen( cartOrderId: Int = 0, navController: NavController, viewModel: AddEditCartOrderViewModel = hiltViewModel(), - resultBackNavigator: ResultBackNavigator + resultBackNavigator: ResultBackNavigator, ) { val lazyListState = rememberLazyListState() @@ -103,7 +107,6 @@ fun AddEditCartOrderScreen( LaunchedEffect(key1 = event) { event?.let { data -> when (data) { - is UiEvent.IsLoading -> {} is UiEvent.OnError -> { resultBackNavigator.navigateBack(data.errorMessage) } @@ -187,6 +190,8 @@ fun AddEditCartOrderScreen( } item(ADDRESS_NAME_FIELD) { + Spacer(modifier = Modifier.height(SpaceSmall)) + AnimatedVisibility( visible = viewModel.state.orderType != OrderType.DineIn, enter = fadeIn(), @@ -266,9 +271,11 @@ fun AddEditCartOrderScreen( ) if (index != addresses.size - 1) { - Divider(modifier = Modifier - .fillMaxWidth() - .padding(start = 44.dp)) + Divider( + modifier = Modifier + .fillMaxWidth() + .padding(start = 44.dp) + ) } } } @@ -313,9 +320,19 @@ fun AddEditCartOrderScreen( customerToggled = true }, trailingIcon = { - ExposedDropdownMenuDefaults.TrailingIcon( - expanded = customerToggled - ) + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + PhoneNoCountBox( + count = newCustomer.customerPhone.length + ) + + Spacer(modifier = Modifier.width(1.dp)) + + ExposedDropdownMenuDefaults.TrailingIcon( + expanded = customerToggled + ) + } }, ) @@ -360,9 +377,11 @@ fun AddEditCartOrderScreen( ) if (index != customers.size - 1) { - Divider(modifier = Modifier - .fillMaxWidth() - .padding(start = 44.dp)) + Divider( + modifier = Modifier + .fillMaxWidth() + .padding(start = 44.dp) + ) } } } diff --git a/feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderState.kt b/feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderState.kt new file mode 100644 index 00000000..ec554c76 --- /dev/null +++ b/feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderState.kt @@ -0,0 +1,8 @@ +package com.niyaj.cartorder.add_edit + +import com.niyaj.model.OrderType + +data class AddEditCartOrderState( + val orderType: OrderType = OrderType.DineIn, + val doesChargesIncluded: Boolean = orderType != OrderType.DineIn, +) diff --git a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderViewModel.kt b/feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderViewModel.kt similarity index 89% rename from app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderViewModel.kt rename to feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderViewModel.kt index cac8b05e..f6f0b023 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/cart_order/presentation/add_edit/AddEditCartOrderViewModel.kt +++ b/feature/cartorder/src/main/java/com/niyaj/cartorder/add_edit/AddEditCartOrderViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.cart_order.presentation.add_edit +package com.niyaj.cartorder.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,17 +7,17 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrder -import com.niyaj.poposroom.features.cart_order.domain.repository.CartOrderRepository -import com.niyaj.poposroom.features.cart_order.domain.repository.CartOrderValidationRepository -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderStatus -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.capitalizeWords -import com.niyaj.poposroom.features.common.utils.getCapitalWord -import com.niyaj.poposroom.features.customer.domain.model.Customer +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.capitalizeWords +import com.niyaj.common.utils.getCapitalWord +import com.niyaj.data.repository.CartOrderRepository +import com.niyaj.data.repository.validation.CartOrderValidationRepository +import com.niyaj.model.Address +import com.niyaj.model.CartOrder +import com.niyaj.model.Customer +import com.niyaj.model.OrderStatus +import com.niyaj.model.OrderType +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow diff --git a/feature/cartorder/src/test/java/com/niyaj/cartorder/ExampleUnitTest.kt b/feature/cartorder/src/test/java/com/niyaj/cartorder/ExampleUnitTest.kt new file mode 100644 index 00000000..f1faa826 --- /dev/null +++ b/feature/cartorder/src/test/java/com/niyaj/cartorder/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.cartorder + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/category/.gitignore b/feature/category/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/category/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/category/build.gradle.kts b/feature/category/build.gradle.kts new file mode 100644 index 00000000..e3310d1e --- /dev/null +++ b/feature/category/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.category" + + ksp { + arg("compose-destinations.moduleName", "category") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/category/proguard-rules.pro b/feature/category/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/category/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/category/src/androidTest/java/com/niyaj/category/ExampleInstrumentedTest.kt b/feature/category/src/androidTest/java/com/niyaj/category/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..5a7a8426 --- /dev/null +++ b/feature/category/src/androidTest/java/com/niyaj/category/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.category + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.category", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/category/src/main/AndroidManifest.xml b/feature/category/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/category/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/CategoryScreen.kt b/feature/category/src/main/java/com/niyaj/category/CategoryScreen.kt similarity index 72% rename from app/src/main/java/com/niyaj/poposroom/features/category/presentation/CategoryScreen.kt rename to feature/category/src/main/java/com/niyaj/category/CategoryScreen.kt index d91b85a5..0c8c8fc5 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/CategoryScreen.kt +++ b/feature/category/src/main/java/com/niyaj/category/CategoryScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.category.presentation +package com.niyaj.category import androidx.activity.compose.BackHandler import androidx.compose.foundation.BorderStroke @@ -17,10 +17,8 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Category import androidx.compose.material3.AlertDialog -import androidx.compose.material3.BottomSheetScaffoldState import androidx.compose.material3.CardDefaults import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHostState @@ -39,37 +37,43 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.CATEGORY_ITEM_TAG -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.CATEGORY_NOT_AVAIlABLE -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.CATEGORY_SCREEN_TITLE -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.CATEGORY_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.CREATE_NEW_CATEGORY -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.DELETE_CATEGORY_ITEM_MESSAGE -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants.DELETE_CATEGORY_ITEM_TITLE -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.Constants.SEARCH_ITEM_NOT_FOUND -import com.niyaj.poposroom.features.common.utils.SheetScreen -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled +import com.niyaj.category.destinations.AddEditCategoryScreenDestination +import com.niyaj.common.utils.Constants.SEARCH_ITEM_NOT_FOUND +import com.niyaj.data.utils.CategoryConstants.CATEGORY_ITEM_TAG +import com.niyaj.data.utils.CategoryConstants.CATEGORY_NOT_AVAIlABLE +import com.niyaj.data.utils.CategoryConstants.CATEGORY_SCREEN_TITLE +import com.niyaj.data.utils.CategoryConstants.CATEGORY_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.CategoryConstants.CREATE_NEW_CATEGORY +import com.niyaj.data.utils.CategoryConstants.DELETE_CATEGORY_ITEM_MESSAGE +import com.niyaj.data.utils.CategoryConstants.DELETE_CATEGORY_ITEM_TITLE +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.Category +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.ramcosta.composedestinations.navigation.navigate +import com.ramcosta.composedestinations.result.NavResult +import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.CategoryScreen +) @Composable fun CategoryScreen( - bottomSheetScaffoldState: BottomSheetScaffoldState, navController: NavController, - onCloseSheet: () -> Unit = {}, - onOpenSheet: (SheetScreen) -> Unit = {}, viewModel: CategoryViewModel = hiltViewModel(), + resultRecipient: ResultRecipient, ) { val scope = rememberCoroutineScope() val snackbarState = remember { SnackbarHostState() } @@ -79,7 +83,7 @@ fun CategoryScreen( val lazyGridState = rememberLazyGridState() - val showFab = viewModel.totalItems.isNotEmpty() + val showFab = viewModel.totalItems.isNotEmpty() val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value @@ -90,13 +94,13 @@ fun CategoryScreen( LaunchedEffect(key1 = event) { event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} + when (data) { is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) } } + is UiEvent.OnSuccess -> { scope.launch { snackbarState.showSnackbar(data.successMessage) @@ -112,27 +116,33 @@ fun CategoryScreen( } else if (showSearchBar) { viewModel.closeSearchBar() } - if (bottomSheetScaffoldState.bottomSheetState.hasExpandedState) { - onCloseSheet() + } + + resultRecipient.onNavResult { result -> + when (result) { + is NavResult.Canceled -> { + viewModel.deselectItems() } + + is NavResult.Value -> { + scope.launch { + viewModel.deselectItems() + snackbarState.showSnackbar(result.value) + } + } + } } StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) CATEGORY_SCREEN_TITLE else "${selectedItems.size} Selected", - placeholderText = CATEGORY_SEARCH_PLACEHOLDER, - showSearchBar = showSearchBar, - selectionCount = selectedItems.size, - searchText = searchText, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyGridState.isScrolled, fabText = CREATE_NEW_CATEGORY, fabVisible = (showFab && selectedItems.isEmpty() && !showSearchBar), onFabClick = { - onOpenSheet(SheetScreen.CreateNewCategory) + navController.navigate(AddEditCategoryScreenDestination()) }, onClickScroll = { scope.launch { @@ -141,19 +151,32 @@ fun CategoryScreen( } ) }, - fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - onOpenSheet(SheetScreen.UpdateCategory(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true + navActions = { + ScaffoldNavActions( + placeholderText = CATEGORY_SEARCH_PLACEHOLDER, + showSettingsIcon = true, + selectionCount = selectedItems.size, + showSearchIcon = showSearchBar, + searchText = searchText, + onEditClick = { + navController.navigate(AddEditCategoryScreenDestination(selectedItems.first())) + }, + onDeleteClick = { + openDialog.value = true + }, + onSettingsClick = {}, + onSelectAllClick = viewModel::selectAllItems, + onClearClick = viewModel::clearSearchText, + onSearchClick = viewModel::openSearchBar, + onSearchTextChanged = viewModel::searchTextChanged + ) }, + fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, - onSearchTextChanged = viewModel::searchTextChanged, - onSearchClick = viewModel::openSearchBar, onBackClick = viewModel::closeSearchBar, - onClearClick = viewModel::clearSearchText + snackbarHostState = snackbarState, ) { _ -> when (state) { is UiState.Loading -> LoadingIndicator() @@ -162,10 +185,11 @@ fun CategoryScreen( text = if (searchText.isEmpty()) CATEGORY_NOT_AVAIlABLE else SEARCH_ITEM_NOT_FOUND, buttonText = CREATE_NEW_CATEGORY, onClick = { - onOpenSheet(SheetScreen.CreateNewCategory) + navController.navigate(AddEditCategoryScreenDestination()) } ) } + is UiState.Success -> { LazyVerticalGrid( modifier = Modifier @@ -175,7 +199,7 @@ fun CategoryScreen( ) { items( items = state.data, - key = { it.categoryId} + key = { it.categoryId } ) { item: Category -> CategoryData( item = item, diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/CategoryViewModel.kt b/feature/category/src/main/java/com/niyaj/category/CategoryViewModel.kt similarity index 77% rename from app/src/main/java/com/niyaj/poposroom/features/category/presentation/CategoryViewModel.kt rename to feature/category/src/main/java/com/niyaj/category/CategoryViewModel.kt index 9cf372af..ccf5a888 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/CategoryViewModel.kt +++ b/feature/category/src/main/java/com/niyaj/category/CategoryViewModel.kt @@ -1,14 +1,14 @@ -package com.niyaj.poposroom.features.category.presentation +package com.niyaj.category import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.category.domain.repository.CategoryRepository -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.CategoryRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -23,7 +23,8 @@ import javax.inject.Inject @HiltViewModel class CategoryViewModel @Inject constructor( private val categoryRepository: CategoryRepository, - @Dispatcher(PoposDispatchers.IO) private val ioDispatcher: CoroutineDispatcher + @Dispatcher(PoposDispatchers.IO) + private val ioDispatcher: CoroutineDispatcher ): BaseViewModel() { override var totalItems: List = emptyList() diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryEvent.kt b/feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryEvent.kt similarity index 62% rename from app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryEvent.kt rename to feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryEvent.kt index aedfe2f8..61166679 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryEvent.kt +++ b/feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryEvent.kt @@ -1,10 +1,10 @@ -package com.niyaj.poposroom.features.category.presentation.add_edit +package com.niyaj.category.add_edit sealed class AddEditCategoryEvent { data class CategoryNameChanged(val categoryName: String) : AddEditCategoryEvent() - object CategoryAvailabilityChanged: AddEditCategoryEvent() + data object CategoryAvailabilityChanged: AddEditCategoryEvent() data class CreateUpdateAddEditCategory(val categoryId: Int = 0) : AddEditCategoryEvent() } diff --git a/feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryScreen.kt b/feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryScreen.kt new file mode 100644 index 00000000..f612392c --- /dev/null +++ b/feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryScreen.kt @@ -0,0 +1,137 @@ +package com.niyaj.category.add_edit + +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.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Category +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.Checkbox +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import com.niyaj.data.utils.AddressTestTags +import com.niyaj.data.utils.CategoryConstants.ADD_EDIT_CATEGORY_SCREEN +import com.niyaj.data.utils.CategoryConstants.CATEGORY_AVAILABLE_SWITCH +import com.niyaj.data.utils.CategoryConstants.CATEGORY_NAME_ERROR_TAG +import com.niyaj.data.utils.CategoryConstants.CATEGORY_NAME_FIELD +import com.niyaj.data.utils.CategoryConstants.CREATE_NEW_CATEGORY +import com.niyaj.data.utils.CategoryConstants.UPDATE_CATEGORY +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.components.StandardTextField +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.result.ResultBackNavigator + +@Destination( + route = Screens.AddEditCategoryScreen +) +@Composable +fun AddEditCategoryScreen( + categoryId: Int = 0, + navController: NavController, + resultBackNavigator: ResultBackNavigator, + viewModel: AddEditCategoryViewModel = hiltViewModel(), +) { + val nameError = viewModel.nameError.collectAsStateWithLifecycle().value + + val enableBtn = nameError == null + + val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value + val title = if (categoryId == 0) CREATE_NEW_CATEGORY else UPDATE_CATEGORY + + LaunchedEffect(key1 = event) { + event?.let { data -> + when (data) { + is UiEvent.OnError -> { + resultBackNavigator.navigateBack(data.errorMessage) + } + + is UiEvent.OnSuccess -> { + resultBackNavigator.navigateBack(data.successMessage) + } + } + } + } + + StandardScaffoldWithOutDrawer( + title = title, + onBackClick = { + navController.navigateUp() + }, + showBottomBar = enableBtn, + bottomBar = { + StandardButton( + modifier = Modifier + .testTag(AddressTestTags.ADD_EDIT_ADDRESS_BTN) + .padding(horizontal = SpaceSmallMax), + text = title, + icon = if (categoryId == 0) Icons.Default.Add else Icons.Default.Edit, + enabled = enableBtn, + onClick = { + viewModel.onEvent(AddEditCategoryEvent.CreateUpdateAddEditCategory(categoryId)) + } + ) + } + ) { + Column( + modifier = Modifier + .testTag(ADD_EDIT_CATEGORY_SCREEN) + .fillMaxWidth() + .padding(SpaceSmall), + verticalArrangement = Arrangement.spacedBy(SpaceSmall), + ) { + StandardTextField( + value = viewModel.addEditState.categoryName, + label = CATEGORY_NAME_FIELD, + leadingIcon = Icons.Default.Category, + isError = nameError != null, + errorText = nameError, + errorTextTag = CATEGORY_NAME_ERROR_TAG, + onValueChange = { + viewModel.onEvent(AddEditCategoryEvent.CategoryNameChanged(it)) + } + ) + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Checkbox( + modifier = Modifier.testTag(CATEGORY_AVAILABLE_SWITCH), + checked = viewModel.addEditState.isAvailable, + onCheckedChange = { + viewModel.onEvent(AddEditCategoryEvent.CategoryAvailabilityChanged) + } + ) + Spacer(modifier = Modifier.width(SpaceSmall)) + Text( + text = if (viewModel.addEditState.isAvailable) + "Marked as available" + else + "Marked as not available", + style = MaterialTheme.typography.labelMedium + ) + } + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryState.kt b/feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryState.kt similarity index 61% rename from app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryState.kt rename to feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryState.kt index b3bf5c2b..fd34da29 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryState.kt +++ b/feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryState.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.category.presentation.add_edit +package com.niyaj.category.add_edit data class AddEditCategoryState( val categoryName: String = "", diff --git a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryViewModel.kt b/feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryViewModel.kt similarity index 82% rename from app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryViewModel.kt rename to feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryViewModel.kt index 62cbc395..a8143fd7 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/category/presentation/add_edit/AddEditCategoryViewModel.kt +++ b/feature/category/src/main/java/com/niyaj/category/add_edit/AddEditCategoryViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.category.presentation.add_edit +package com.niyaj.category.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,13 +7,13 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.category.domain.repository.CategoryRepository -import com.niyaj.poposroom.features.category.domain.repository.CategoryValidationRepository -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.CategoryRepository +import com.niyaj.data.repository.validation.CategoryValidationRepository +import com.niyaj.model.Category +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,7 +33,7 @@ class AddEditCategoryViewModel @Inject constructor( private val categoryRepository: CategoryRepository, private val validationRepository: CategoryValidationRepository, @Dispatcher(PoposDispatchers.IO) private val ioDispatcher: CoroutineDispatcher, - savedStateHandle: SavedStateHandle + savedStateHandle: SavedStateHandle, ) : ViewModel() { private var categoryId = savedStateHandle.get("addOnItemId") @@ -75,12 +75,13 @@ class AddEditCategoryViewModel @Inject constructor( } } - fun getCategoryById(itemId: Int) { + private fun getCategoryById(itemId: Int) { viewModelScope.launch(ioDispatcher) { - when(val result = categoryRepository.getCategoryById(itemId)){ + when (val result = categoryRepository.getCategoryById(itemId)) { is Resource.Error -> { _eventFlow.emit(UiEvent.OnError(result.message ?: "unable")) } + is Resource.Success -> { result.data?.let { category -> categoryId = category.categoryId @@ -95,10 +96,6 @@ class AddEditCategoryViewModel @Inject constructor( } } - fun resetFields() { - addEditState = AddEditCategoryState() - } - private fun createOrUpdateCategory(categoryId: Int = 0) { viewModelScope.launch(ioDispatcher) { if (nameError.value == null) { @@ -110,10 +107,11 @@ class AddEditCategoryViewModel @Inject constructor( updatedAt = if (categoryId != 0) Date() else null ) - when(categoryRepository.upsertCategory(addOnItem)) { + when (categoryRepository.upsertCategory(addOnItem)) { is Resource.Error -> { _eventFlow.emit(UiEvent.OnError("Unable To Create Category.")) } + is Resource.Success -> { _eventFlow.emit(UiEvent.OnSuccess("Category Created Successfully.")) } diff --git a/feature/category/src/test/java/com/niyaj/category/ExampleUnitTest.kt b/feature/category/src/test/java/com/niyaj/category/ExampleUnitTest.kt new file mode 100644 index 00000000..7fdf6d95 --- /dev/null +++ b/feature/category/src/test/java/com/niyaj/category/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.category + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/charges/.gitignore b/feature/charges/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/charges/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/charges/build.gradle.kts b/feature/charges/build.gradle.kts new file mode 100644 index 00000000..788bef81 --- /dev/null +++ b/feature/charges/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.charges" + + ksp { + arg("compose-destinations.moduleName", "charges") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/charges/proguard-rules.pro b/feature/charges/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/charges/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/charges/src/androidTest/java/com/niyaj/charges/ExampleInstrumentedTest.kt b/feature/charges/src/androidTest/java/com/niyaj/charges/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..3c22dd20 --- /dev/null +++ b/feature/charges/src/androidTest/java/com/niyaj/charges/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.charges + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.charges", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/charges/src/main/AndroidManifest.xml b/feature/charges/src/main/AndroidManifest.xml new file mode 100644 index 00000000..de87c72a --- /dev/null +++ b/feature/charges/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/ChargesScreen.kt b/feature/charges/src/main/java/com/niyaj/charges/ChargesScreen.kt similarity index 71% rename from app/src/main/java/com/niyaj/poposroom/features/charges/presentation/ChargesScreen.kt rename to feature/charges/src/main/java/com/niyaj/charges/ChargesScreen.kt index 4232c7cb..e3a58b21 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/ChargesScreen.kt +++ b/feature/charges/src/main/java/com/niyaj/charges/ChargesScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.charges.presentation +package com.niyaj.charges import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Arrangement @@ -16,8 +16,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Bolt import androidx.compose.material3.AlertDialog -import androidx.compose.material3.BottomSheetScaffoldState -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text @@ -33,38 +31,44 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CHARGES_NOT_AVAIlABLE -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CHARGES_SCREEN_TITLE -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CHARGES_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CHARGES_TAG -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.CREATE_NEW_CHARGES -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.DELETE_CHARGES_MESSAGE -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.DELETE_CHARGES_TITLE -import com.niyaj.poposroom.features.charges.domain.utils.ChargesTestTags.NO_ITEMS_IN_CHARGES -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardElevatedCard -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.SheetScreen -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.common.utils.toRupee +import com.niyaj.charges.destinations.AddEditChargesScreenDestination +import com.niyaj.common.utils.toRupee +import com.niyaj.data.utils.ChargesTestTags.CHARGES_NOT_AVAIlABLE +import com.niyaj.data.utils.ChargesTestTags.CHARGES_SCREEN_TITLE +import com.niyaj.data.utils.ChargesTestTags.CHARGES_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.ChargesTestTags.CHARGES_TAG +import com.niyaj.data.utils.ChargesTestTags.CREATE_NEW_CHARGES +import com.niyaj.data.utils.ChargesTestTags.DELETE_CHARGES_MESSAGE +import com.niyaj.data.utils.ChargesTestTags.DELETE_CHARGES_TITLE +import com.niyaj.data.utils.ChargesTestTags.NO_ITEMS_IN_CHARGES +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.Charges +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardElevatedCard +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.ramcosta.composedestinations.navigation.navigate +import com.ramcosta.composedestinations.result.NavResult +import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.ChargesScreen +) @Composable fun ChargesScreen( - bottomSheetScaffoldState: BottomSheetScaffoldState, navController: NavController, - onCloseSheet: () -> Unit = {}, - onOpenSheet: (SheetScreen) -> Unit = {}, + resultRecipient: ResultRecipient, viewModel: ChargesViewModel = hiltViewModel(), ) { val scope = rememberCoroutineScope() @@ -86,13 +90,13 @@ fun ChargesScreen( LaunchedEffect(key1 = event) { event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} + when (data) { is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) } } + is UiEvent.OnSuccess -> { scope.launch { snackbarState.showSnackbar(data.successMessage) @@ -102,34 +106,40 @@ fun ChargesScreen( } } + resultRecipient.onNavResult { result -> + when (result) { + is NavResult.Canceled -> { + viewModel.deselectItems() + } + + is NavResult.Value -> { + scope.launch { + viewModel.deselectItems() + snackbarState.showSnackbar(result.value) + } + } + } + } + BackHandler { if (selectedItems.isNotEmpty()) { viewModel.deselectItems() } else if (showSearchBar) { viewModel.closeSearchBar() } - if (bottomSheetScaffoldState.bottomSheetState.hasExpandedState) { - onCloseSheet() - } } StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) CHARGES_SCREEN_TITLE else "${selectedItems.size} Selected", - placeholderText = CHARGES_SEARCH_PLACEHOLDER, - showSearchBar = showSearchBar, - showSettings = false, - selectionCount = selectedItems.size, - searchText = searchText, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyGridState.isScrolled, fabText = CREATE_NEW_CHARGES, fabVisible = (showFab && selectedItems.isEmpty() && !showSearchBar), onFabClick = { - onOpenSheet(SheetScreen.CreateNewCharges) + navController.navigate(AddEditChargesScreenDestination()) + }, onClickScroll = { scope.launch { @@ -138,19 +148,32 @@ fun ChargesScreen( } ) }, - fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - onOpenSheet(SheetScreen.UpdateCharges(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true + navActions = { + ScaffoldNavActions( + placeholderText = CHARGES_SEARCH_PLACEHOLDER, + showSettingsIcon = true, + selectionCount = selectedItems.size, + showSearchIcon = showSearchBar, + searchText = searchText, + onEditClick = { + navController.navigate(AddEditChargesScreenDestination(selectedItems.first())) + }, + onDeleteClick = { + openDialog.value = true + }, + onSettingsClick = {}, + onSelectAllClick = viewModel::selectAllItems, + onClearClick = viewModel::clearSearchText, + onSearchClick = viewModel::openSearchBar, + onSearchTextChanged = viewModel::searchTextChanged + ) }, + fabPosition = if (lazyGridState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, - onSearchTextChanged = viewModel::searchTextChanged, - onSearchClick = viewModel::openSearchBar, onBackClick = viewModel::closeSearchBar, - onClearClick = viewModel::clearSearchText + snackbarHostState = snackbarState, ) { _ -> when (state) { is UiState.Loading -> LoadingIndicator() @@ -159,10 +182,11 @@ fun ChargesScreen( text = if (searchText.isEmpty()) CHARGES_NOT_AVAIlABLE else NO_ITEMS_IN_CHARGES, buttonText = CREATE_NEW_CHARGES, onClick = { - onOpenSheet(SheetScreen.CreateNewCharges) + navController.navigate(AddEditChargesScreenDestination()) } ) } + is UiState.Success -> { LazyVerticalGrid( modifier = Modifier @@ -172,7 +196,7 @@ fun ChargesScreen( ) { items( items = state.data, - key = { it.chargesId} + key = { it.chargesId } ) { item: Charges -> ChargesData( item = item, diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/ChargesViewModel.kt b/feature/charges/src/main/java/com/niyaj/charges/ChargesViewModel.kt similarity index 79% rename from app/src/main/java/com/niyaj/poposroom/features/charges/presentation/ChargesViewModel.kt rename to feature/charges/src/main/java/com/niyaj/charges/ChargesViewModel.kt index 82de551f..11f2c9f3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/ChargesViewModel.kt +++ b/feature/charges/src/main/java/com/niyaj/charges/ChargesViewModel.kt @@ -1,14 +1,14 @@ -package com.niyaj.poposroom.features.charges.presentation +package com.niyaj.charges import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.charges.domain.repository.ChargesRepository -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.ChargesRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesEvent.kt b/feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesEvent.kt similarity index 70% rename from app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesEvent.kt rename to feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesEvent.kt index 39e279b6..32327b33 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesEvent.kt +++ b/feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesEvent.kt @@ -1,12 +1,13 @@ -package com.niyaj.poposroom.features.charges.presentation.add_edit +package com.niyaj.charges.add_edit sealed interface AddEditChargesEvent { + data class ChargesNameChanged(val chargesName: String) : AddEditChargesEvent data class ChargesPriceChanged(val chargesPrice: String) : AddEditChargesEvent - object ChargesApplicableChanged: AddEditChargesEvent + data object ChargesApplicableChanged: AddEditChargesEvent data class CreateOrUpdateCharges(val chargesId: Int = 0) : AddEditChargesEvent } \ No newline at end of file diff --git a/feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesScreen.kt b/feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesScreen.kt new file mode 100644 index 00000000..af0cf496 --- /dev/null +++ b/feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesScreen.kt @@ -0,0 +1,153 @@ +package com.niyaj.charges.add_edit + +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.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Category +import androidx.compose.material.icons.filled.CurrencyRupee +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material3.Checkbox +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.input.KeyboardType +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import com.niyaj.common.utils.safeString +import com.niyaj.data.utils.ChargesTestTags.ADD_EDIT_CHARGES_BUTTON +import com.niyaj.data.utils.ChargesTestTags.ADD_EDIT_CHARGES_SCREEN +import com.niyaj.data.utils.ChargesTestTags.CHARGES_AMOUNT_ERROR +import com.niyaj.data.utils.ChargesTestTags.CHARGES_AMOUNT_FIELD +import com.niyaj.data.utils.ChargesTestTags.CHARGES_APPLIED_SWITCH +import com.niyaj.data.utils.ChargesTestTags.CHARGES_NAME_ERROR +import com.niyaj.data.utils.ChargesTestTags.CHARGES_NAME_FIELD +import com.niyaj.data.utils.ChargesTestTags.CREATE_NEW_CHARGES +import com.niyaj.data.utils.ChargesTestTags.EDIT_CHARGES_ITEM +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.utils.UiEvent +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.result.ResultBackNavigator + +@Destination +@Composable +fun AddEditChargesScreen( + chargesId: Int = 0, + navController: NavController, + resultBackNavigator: ResultBackNavigator, + viewModel: AddEditChargesViewModel = hiltViewModel(), +) { + val nameError = viewModel.nameError.collectAsStateWithLifecycle().value + val priceError = viewModel.priceError.collectAsStateWithLifecycle().value + + val enableBtn = nameError == null && priceError == null + + val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value + val title = if (chargesId == 0) CREATE_NEW_CHARGES else EDIT_CHARGES_ITEM + + LaunchedEffect(key1 = event) { + event?.let { data -> + when (data) { + is UiEvent.OnError -> { + resultBackNavigator.navigateBack(data.errorMessage) + } + + is UiEvent.OnSuccess -> { + resultBackNavigator.navigateBack(data.successMessage) + } + } + } + } + + StandardScaffoldWithOutDrawer( + title = title, + onBackClick = { + navController.navigateUp() + }, + showBottomBar = enableBtn, + bottomBar = { + StandardButton( + modifier = Modifier + .testTag(ADD_EDIT_CHARGES_BUTTON) + .padding(horizontal = SpaceSmallMax), + text = title, + icon = if (chargesId == 0) Icons.Default.Add else Icons.Default.Edit, + enabled = enableBtn, + onClick = { + viewModel.onEvent(AddEditChargesEvent.CreateOrUpdateCharges(chargesId)) + } + ) + } + ) { + Column( + modifier = Modifier + .testTag(ADD_EDIT_CHARGES_SCREEN) + .fillMaxWidth() + .padding(SpaceSmall), + verticalArrangement = Arrangement.spacedBy(SpaceSmall), + ) { + StandardOutlinedTextField( + value = viewModel.addEditState.chargesName, + label = CHARGES_NAME_FIELD, + leadingIcon = Icons.Default.Category, + isError = nameError != null, + errorText = nameError, + errorTextTag = CHARGES_NAME_ERROR, + onValueChange = { + viewModel.onEvent(AddEditChargesEvent.ChargesNameChanged(it)) + } + ) + + StandardOutlinedTextField( + value = viewModel.addEditState.chargesPrice.safeString, + label = CHARGES_AMOUNT_FIELD, + leadingIcon = Icons.Default.CurrencyRupee, + isError = priceError != null, + errorText = priceError, + keyboardType = KeyboardType.Number, + errorTextTag = CHARGES_AMOUNT_ERROR, + onValueChange = { + viewModel.onEvent(AddEditChargesEvent.ChargesPriceChanged(it)) + } + ) + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + ) { + Checkbox( + modifier = Modifier.testTag(CHARGES_APPLIED_SWITCH), + checked = viewModel.addEditState.chargesApplicable, + onCheckedChange = { + viewModel.onEvent(AddEditChargesEvent.ChargesApplicableChanged) + } + ) + Spacer(modifier = Modifier.width(SpaceSmall)) + Text( + text = if (viewModel.addEditState.chargesApplicable) + "Marked as applied" + else + "Marked as not applied", + style = MaterialTheme.typography.labelMedium + ) + } + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesState.kt b/feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesState.kt similarity index 68% rename from app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesState.kt rename to feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesState.kt index bb25fe7f..c3d9d072 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesState.kt +++ b/feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesState.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.charges.presentation.add_edit +package com.niyaj.charges.add_edit data class AddEditChargesState( val chargesName: String = "", diff --git a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesViewModel.kt b/feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesViewModel.kt similarity index 64% rename from app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesViewModel.kt rename to feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesViewModel.kt index 1c3b4f21..ecce9b39 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/charges/presentation/add_edit/AddEditChargesViewModel.kt +++ b/feature/charges/src/main/java/com/niyaj/charges/add_edit/AddEditChargesViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.charges.presentation.add_edit +package com.niyaj.charges.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,16 +7,13 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.charges.domain.repository.ChargesRepository -import com.niyaj.poposroom.features.charges.domain.repository.ChargesValidationRepository -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.safeInt +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.safeInt +import com.niyaj.data.repository.ChargesRepository +import com.niyaj.data.repository.validation.ChargesValidationRepository +import com.niyaj.model.Charges +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharingStarted @@ -33,11 +30,10 @@ import javax.inject.Inject class AddEditChargesViewModel @Inject constructor( private val chargesRepository: ChargesRepository, private val validationRepository: ChargesValidationRepository, - @Dispatcher(PoposDispatchers.IO) private val ioDispatcher: CoroutineDispatcher, - savedStateHandle: SavedStateHandle -): ViewModel() { + savedStateHandle: SavedStateHandle, +) : ViewModel() { - private var chargesId = savedStateHandle.get("addOnItemId") + private val chargesId = savedStateHandle.get("chargesId") ?: 0 var addEditState by mutableStateOf(AddEditChargesState()) @@ -45,7 +41,7 @@ class AddEditChargesViewModel @Inject constructor( val eventFlow = _eventFlow.asSharedFlow() init { - savedStateHandle.get("addOnItemId")?.let { addOnItemId -> + savedStateHandle.get("chargesId")?.let { addOnItemId -> getChargesById(addOnItemId) } } @@ -80,7 +76,8 @@ class AddEditChargesViewModel @Inject constructor( } is AddEditChargesEvent.ChargesApplicableChanged -> { - addEditState = addEditState.copy(chargesApplicable = !addEditState.chargesApplicable) + addEditState = + addEditState.copy(chargesApplicable = !addEditState.chargesApplicable) } is AddEditChargesEvent.CreateOrUpdateCharges -> { @@ -89,33 +86,30 @@ class AddEditChargesViewModel @Inject constructor( } } - fun getChargesById(itemId: Int) { - viewModelScope.launch(ioDispatcher) { - when(val result = chargesRepository.getChargesById(itemId)) { - is Resource.Error -> { - _eventFlow.emit(UiEvent.OnError("Unable to find charges")) - } - is Resource.Success -> { - result.data?.let { charges -> - chargesId = charges.chargesId - - addEditState = addEditState.copy( - chargesName = charges.chargesName, - chargesPrice = charges.chargesPrice, - chargesApplicable = charges.isApplicable - ) + private fun getChargesById(itemId: Int) { + if (itemId != 0) { + viewModelScope.launch { + when (val result = chargesRepository.getChargesById(itemId)) { + is Resource.Error -> { + _eventFlow.emit(UiEvent.OnError("Unable to find charges")) + } + + is Resource.Success -> { + result.data?.let { charges -> + addEditState = addEditState.copy( + chargesName = charges.chargesName, + chargesPrice = charges.chargesPrice, + chargesApplicable = charges.isApplicable + ) + } } } } } } - fun resetFields() { - addEditState = AddEditChargesState() - } - private fun createOrUpdateCharges(chargesId: Int = 0) { - viewModelScope.launch(ioDispatcher) { + viewModelScope.launch { if (nameError.value == null && priceError.value == null) { val addOnItem = Charges( chargesId = chargesId, @@ -126,11 +120,12 @@ class AddEditChargesViewModel @Inject constructor( updatedAt = if (chargesId != 0) Date() else null ) - when(chargesRepository.upsertCharges(addOnItem)) { + when (chargesRepository.upsertCharges(addOnItem)) { is Resource.Error -> { _eventFlow.emit(UiEvent.OnError("Unable To Create Charges.")) } + is Resource.Success -> { _eventFlow.emit(UiEvent.OnSuccess("Charges Created Successfully.")) diff --git a/feature/charges/src/test/java/com/niyaj/charges/ExampleUnitTest.kt b/feature/charges/src/test/java/com/niyaj/charges/ExampleUnitTest.kt new file mode 100644 index 00000000..efcd484c --- /dev/null +++ b/feature/charges/src/test/java/com/niyaj/charges/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.charges + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/customer/.gitignore b/feature/customer/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/customer/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/customer/build.gradle.kts b/feature/customer/build.gradle.kts new file mode 100644 index 00000000..c16ff5f3 --- /dev/null +++ b/feature/customer/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.customer" + + ksp { + arg("compose-destinations.moduleName", "customer") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/customer/proguard-rules.pro b/feature/customer/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/customer/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/customer/src/androidTest/java/com/niyaj/customer/ExampleInstrumentedTest.kt b/feature/customer/src/androidTest/java/com/niyaj/customer/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..ddf9338a --- /dev/null +++ b/feature/customer/src/androidTest/java/com/niyaj/customer/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.customer + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.customer", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/customer/src/main/AndroidManifest.xml b/feature/customer/src/main/AndroidManifest.xml new file mode 100644 index 00000000..de87c72a --- /dev/null +++ b/feature/customer/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/CustomerScreen.kt b/feature/customer/src/main/java/com/niyaj/customer/CustomerScreen.kt similarity index 58% rename from app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/CustomerScreen.kt rename to feature/customer/src/main/java/com/niyaj/customer/CustomerScreen.kt index 1e3e53c8..fe2aa632 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/CustomerScreen.kt +++ b/feature/customer/src/main/java/com/niyaj/customer/CustomerScreen.kt @@ -1,6 +1,7 @@ -package com.niyaj.poposroom.features.customer.presentaion +package com.niyaj.customer import androidx.activity.compose.BackHandler +import androidx.compose.animation.Crossfade import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.border @@ -11,14 +12,12 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowRight import androidx.compose.material.icons.filled.Person import androidx.compose.material3.AlertDialog -import androidx.compose.material3.BottomSheetScaffoldState -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition +import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHostState @@ -36,42 +35,48 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.SheetScreen -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.customer.domain.model.Customer -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CREATE_NEW_CUSTOMER -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_NOT_AVAIlABLE -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_SCREEN_TITLE -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.CUSTOMER_TAG -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.DELETE_CUSTOMER_MESSAGE -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.DELETE_CUSTOMER_TITLE -import com.niyaj.poposroom.features.customer.domain.utils.CustomerTestTags.NO_ITEMS_IN_CUSTOMER +import com.niyaj.customer.destinations.AddEditCustomerScreenDestination +import com.niyaj.data.utils.CustomerTestTags.CREATE_NEW_CUSTOMER +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_NOT_AVAIlABLE +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_SCREEN_TITLE +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_TAG +import com.niyaj.data.utils.CustomerTestTags.DELETE_CUSTOMER_MESSAGE +import com.niyaj.data.utils.CustomerTestTags.DELETE_CUSTOMER_TITLE +import com.niyaj.data.utils.CustomerTestTags.NO_ITEMS_IN_CUSTOMER +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.Customer +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.ramcosta.composedestinations.navigation.navigate +import com.ramcosta.composedestinations.result.NavResult +import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch -@OptIn(ExperimentalMaterial3Api::class) -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.CustomerScreen +) @Composable fun CustomerScreen( - bottomSheetScaffoldState: BottomSheetScaffoldState, navController: NavController, - onCloseSheet: () -> Unit = {}, - onOpenSheet: (SheetScreen) -> Unit = {}, + resultRecipient: ResultRecipient, viewModel: CustomerViewModel = hiltViewModel(), ) { val scope = rememberCoroutineScope() val snackbarState = remember { SnackbarHostState() } - val state = viewModel.charges.collectAsStateWithLifecycle().value + val uiState = viewModel.charges.collectAsStateWithLifecycle().value val selectedItems = viewModel.selectedItems.toList() @@ -88,13 +93,13 @@ fun CustomerScreen( LaunchedEffect(key1 = event) { event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} + when (data) { is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) } } + is UiEvent.OnSuccess -> { scope.launch { snackbarState.showSnackbar(data.successMessage) @@ -104,33 +109,39 @@ fun CustomerScreen( } } + resultRecipient.onNavResult { result -> + when (result) { + is NavResult.Canceled -> { + viewModel.deselectItems() + } + + is NavResult.Value -> { + scope.launch { + viewModel.deselectItems() + snackbarState.showSnackbar(result.value) + } + } + } + } + BackHandler { if (selectedItems.isNotEmpty()) { viewModel.deselectItems() } else if (showSearchBar) { viewModel.closeSearchBar() } - if (bottomSheetScaffoldState.bottomSheetState.hasExpandedState) { - onCloseSheet() - } } StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) CUSTOMER_SCREEN_TITLE else "${selectedItems.size} Selected", - placeholderText = CUSTOMER_SEARCH_PLACEHOLDER, - showSearchBar = showSearchBar, - selectionCount = selectedItems.size, - searchText = searchText, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyListState.isScrolled, fabText = CREATE_NEW_CUSTOMER, fabVisible = (showFab && selectedItems.isEmpty() && !showSearchBar), onFabClick = { - onOpenSheet(SheetScreen.CreateNewCustomer) + navController.navigate(AddEditCustomerScreenDestination()) }, onClickScroll = { scope.launch { @@ -139,53 +150,72 @@ fun CustomerScreen( } ) }, - fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - onOpenSheet(SheetScreen.UpdateCustomer(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true + navActions = { + ScaffoldNavActions( + placeholderText = CUSTOMER_SEARCH_PLACEHOLDER, + showSettingsIcon = true, + selectionCount = selectedItems.size, + showSearchIcon = showSearchBar, + searchText = searchText, + onEditClick = { + navController.navigate(AddEditCustomerScreenDestination(selectedItems.first())) + }, + onDeleteClick = { + openDialog.value = true + }, + onSettingsClick = {}, + onSelectAllClick = viewModel::selectAllItems, + onClearClick = viewModel::clearSearchText, + onSearchClick = viewModel::openSearchBar, + onSearchTextChanged = viewModel::searchTextChanged + ) }, + fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, - onSearchTextChanged = viewModel::searchTextChanged, - onSearchClick = viewModel::openSearchBar, onBackClick = viewModel::closeSearchBar, - onClearClick = viewModel::clearSearchText + snackbarHostState = snackbarState, ) { _ -> - when (state) { - is UiState.Loading -> LoadingIndicator() - is UiState.Empty -> { - ItemNotAvailable( - text = if (searchText.isEmpty()) CUSTOMER_NOT_AVAIlABLE else NO_ITEMS_IN_CUSTOMER, - buttonText = CREATE_NEW_CUSTOMER, - onClick = { - onOpenSheet(SheetScreen.CreateNewCustomer) - } - ) - } - is UiState.Success -> { - LazyColumn( - modifier = Modifier - .padding(SpaceSmall), - state = lazyListState - ) { - items( - items = state.data, - key = { it.customerId} - ) { item: Customer -> - CustomerData( - item = item, - doesSelected = { - selectedItems.contains(it) - }, - onClick = { - if (selectedItems.isNotEmpty()) { - viewModel.selectItem(it) - } - }, - onLongClick = viewModel::selectItem - ) + Crossfade( + targetState = uiState, + label = "Customer State" + ) { state -> + when (state) { + is UiState.Loading -> LoadingIndicator() + is UiState.Empty -> { + ItemNotAvailable( + text = if (searchText.isEmpty()) CUSTOMER_NOT_AVAIlABLE else NO_ITEMS_IN_CUSTOMER, + buttonText = CREATE_NEW_CUSTOMER, + onClick = { + navController.navigate(AddEditCustomerScreenDestination()) + } + ) + } + + is UiState.Success -> { + LazyColumn( + modifier = Modifier + .padding(SpaceSmall), + state = lazyListState + ) { + items( + items = state.data, + key = { it.customerId } + ) { item: Customer -> + CustomerData( + item = item, + doesSelected = { + selectedItems.contains(it) + }, + onClick = { + if (selectedItems.isNotEmpty()) { + viewModel.selectItem(it) + } + }, + onLongClick = viewModel::selectItem + ) + } } } } @@ -269,9 +299,9 @@ fun CustomerData( }, supportingContent = item.customerName?.let { { - Text( - text = it - ) + Text( + text = it + ) } }, leadingContent = { diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/CustomerViewModel.kt b/feature/customer/src/main/java/com/niyaj/customer/CustomerViewModel.kt similarity index 80% rename from app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/CustomerViewModel.kt rename to feature/customer/src/main/java/com/niyaj/customer/CustomerViewModel.kt index 0789a409..4f474fac 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/CustomerViewModel.kt +++ b/feature/customer/src/main/java/com/niyaj/customer/CustomerViewModel.kt @@ -1,14 +1,14 @@ -package com.niyaj.poposroom.features.customer.presentaion +package com.niyaj.customer import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.customer.domain.repository.CustomerRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.CustomerRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerEvent.kt b/feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerEvent.kt similarity index 92% rename from app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerEvent.kt rename to feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerEvent.kt index 9532a763..4946ec04 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerEvent.kt +++ b/feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerEvent.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.customer.presentaion.add_edit +package com.niyaj.customer.add_edit sealed interface AddEditCustomerEvent { diff --git a/feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerScreen.kt b/feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerScreen.kt new file mode 100644 index 00000000..b77501ca --- /dev/null +++ b/feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerScreen.kt @@ -0,0 +1,141 @@ +package com.niyaj.customer.add_edit + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +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.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.Email +import androidx.compose.material.icons.filled.Person +import androidx.compose.material.icons.filled.PhoneAndroid +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.text.input.KeyboardType +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import com.niyaj.data.utils.CustomerTestTags.ADD_EDIT_CUSTOMER_BUTTON +import com.niyaj.data.utils.CustomerTestTags.ADD_EDIT_CUSTOMER_SCREEN +import com.niyaj.data.utils.CustomerTestTags.CREATE_NEW_CUSTOMER +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_EMAIL_ERROR +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_EMAIL_FIELD +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_NAME_ERROR +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_NAME_FIELD +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_PHONE_ERROR +import com.niyaj.data.utils.CustomerTestTags.CUSTOMER_PHONE_FIELD +import com.niyaj.data.utils.CustomerTestTags.EDIT_CUSTOMER_ITEM +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.components.StandardTextField +import com.niyaj.ui.utils.UiEvent +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.result.ResultBackNavigator + +@Destination +@Composable +fun AddEditCustomerScreen( + customerId: Int = 0, + navController: NavController, + resultBackNavigator: ResultBackNavigator, + viewModel: AddEditCustomerViewModel = hiltViewModel(), +) { + val phoneError = viewModel.phoneError.collectAsStateWithLifecycle().value + val nameError = viewModel.nameError.collectAsStateWithLifecycle().value + val emailError = viewModel.emailError.collectAsStateWithLifecycle().value + + val enableBtn = phoneError == null && nameError == null && emailError == null + + val event = viewModel.eventFlow.collectAsStateWithLifecycle(initialValue = null).value + val title = if (customerId == 0) CREATE_NEW_CUSTOMER else EDIT_CUSTOMER_ITEM + + LaunchedEffect(key1 = event) { + event?.let { data -> + when(data) { + is UiEvent.OnError -> { + resultBackNavigator.navigateBack(data.errorMessage) + } + is UiEvent.OnSuccess -> { + resultBackNavigator.navigateBack(data.successMessage) + } + } + } + } + + StandardScaffoldWithOutDrawer( + title = title, + onBackClick = { + navController.navigateUp() + }, + showBottomBar = enableBtn, + bottomBar = { + StandardButton( + modifier = Modifier + .testTag(ADD_EDIT_CUSTOMER_BUTTON) + .padding(horizontal = SpaceSmallMax), + text = title, + icon = if (customerId == 0) Icons.Default.Add else Icons.Default.Edit, + enabled = enableBtn, + onClick = { + viewModel.onEvent(AddEditCustomerEvent.CreateOrUpdateCustomer(customerId)) + } + ) + } + ) { + Column( + modifier = Modifier + .testTag(ADD_EDIT_CUSTOMER_SCREEN) + .fillMaxWidth() + .padding(SpaceSmall), + verticalArrangement = Arrangement.spacedBy(SpaceSmall), + ) { + StandardTextField( + value = viewModel.addEditState.customerPhone, + label = CUSTOMER_PHONE_FIELD, + leadingIcon = Icons.Default.PhoneAndroid, + isError = phoneError != null, + errorText = phoneError, + errorTextTag = CUSTOMER_PHONE_ERROR, + keyboardType = KeyboardType.Number, + onValueChange = { + viewModel.onEvent(AddEditCustomerEvent.CustomerPhoneChanged(it)) + } + ) + + + StandardTextField( + value = viewModel.addEditState.customerName ?: "", + label = CUSTOMER_NAME_FIELD, + leadingIcon = Icons.Default.Person, + isError = nameError != null, + errorText = nameError, + errorTextTag = CUSTOMER_NAME_ERROR, + onValueChange = { + viewModel.onEvent(AddEditCustomerEvent.CustomerNameChanged(it)) + } + ) + + + StandardTextField( + value = viewModel.addEditState.customerEmail ?: "", + label = CUSTOMER_EMAIL_FIELD, + leadingIcon = Icons.Default.Email, + isError = emailError != null, + errorText = emailError, + errorTextTag = CUSTOMER_EMAIL_ERROR, + onValueChange = { + viewModel.onEvent(AddEditCustomerEvent.CustomerEmailChanged(it)) + } + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerState.kt b/feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerState.kt similarity index 68% rename from app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerState.kt rename to feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerState.kt index efb294e4..15bb9cd9 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerState.kt +++ b/feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerState.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.customer.presentaion.add_edit +package com.niyaj.customer.add_edit data class AddEditCustomerState( val customerPhone: String = "", diff --git a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerViewModel.kt b/feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerViewModel.kt similarity index 90% rename from app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerViewModel.kt rename to feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerViewModel.kt index bfe6e343..c3e7147f 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/customer/presentaion/add_edit/AddEditCustomerViewModel.kt +++ b/feature/customer/src/main/java/com/niyaj/customer/add_edit/AddEditCustomerViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.customer.presentaion.add_edit +package com.niyaj.customer.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,13 +7,13 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.customer.domain.model.Customer -import com.niyaj.poposroom.features.customer.domain.repository.CustomerRepository -import com.niyaj.poposroom.features.customer.domain.repository.CustomerValidationRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.CustomerRepository +import com.niyaj.data.repository.validation.CustomerValidationRepository +import com.niyaj.model.Customer +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/feature/customer/src/test/java/com/niyaj/customer/ExampleUnitTest.kt b/feature/customer/src/test/java/com/niyaj/customer/ExampleUnitTest.kt new file mode 100644 index 00000000..37632dc4 --- /dev/null +++ b/feature/customer/src/test/java/com/niyaj/customer/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.customer + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/employee/.gitignore b/feature/employee/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/employee/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/employee/build.gradle.kts b/feature/employee/build.gradle.kts new file mode 100644 index 00000000..e3e275f0 --- /dev/null +++ b/feature/employee/build.gradle.kts @@ -0,0 +1,32 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.employee" + + ksp { + arg("compose-destinations.moduleName", "employee") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + implementation(libs.dialog.core) + implementation(libs.dialog.datetime) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/employee/proguard-rules.pro b/feature/employee/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/employee/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/employee/src/androidTest/java/com/niyaj/employee/ExampleInstrumentedTest.kt b/feature/employee/src/androidTest/java/com/niyaj/employee/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..befaa00d --- /dev/null +++ b/feature/employee/src/androidTest/java/com/niyaj/employee/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.employee + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.employee", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/employee/src/main/AndroidManifest.xml b/feature/employee/src/main/AndroidManifest.xml new file mode 100644 index 00000000..fa57d87f --- /dev/null +++ b/feature/employee/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/EmployeeScreen.kt b/feature/employee/src/main/java/com/niyaj/employee/EmployeeScreen.kt similarity index 78% rename from app/src/main/java/com/niyaj/poposroom/features/employee/presentation/EmployeeScreen.kt rename to feature/employee/src/main/java/com/niyaj/employee/EmployeeScreen.kt index f0a355a3..123fe238 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/EmployeeScreen.kt +++ b/feature/employee/src/main/java/com/niyaj/employee/EmployeeScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee.presentation +package com.niyaj.employee import androidx.activity.compose.BackHandler import androidx.compose.foundation.BorderStroke @@ -11,13 +11,14 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowRight import androidx.compose.material.icons.filled.Person import androidx.compose.material3.AlertDialog import androidx.compose.material3.FabPosition +import androidx.compose.material3.Icon import androidx.compose.material3.ListItem +import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text @@ -34,33 +35,40 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.destinations.AddEditEmployeeScreenDestination -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.CREATE_NEW_EMPLOYEE -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.DELETE_EMPLOYEE_MESSAGE -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.DELETE_EMPLOYEE_TITLE -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_NOT_AVAIlABLE -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_SCREEN_TITLE -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_TAG -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.NO_ITEMS_IN_EMPLOYEE +import com.niyaj.data.utils.EmployeeTestTags.CREATE_NEW_EMPLOYEE +import com.niyaj.data.utils.EmployeeTestTags.DELETE_EMPLOYEE_MESSAGE +import com.niyaj.data.utils.EmployeeTestTags.DELETE_EMPLOYEE_TITLE +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_NOT_AVAIlABLE +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_SCREEN_TITLE +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_TAG +import com.niyaj.data.utils.EmployeeTestTags.NO_ITEMS_IN_EMPLOYEE +import com.niyaj.designsystem.theme.LightColor9 +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.employee.destinations.AddEditEmployeeScreenDestination +import com.niyaj.model.Employee +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.navigate import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.EmployeeScreen +) @Composable fun EmployeeScreen( navController: NavController, @@ -87,7 +95,6 @@ fun EmployeeScreen( LaunchedEffect(key1 = event) { event?.let { data -> when(data) { - is UiEvent.IsLoading -> {} is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) @@ -125,13 +132,7 @@ fun EmployeeScreen( StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) EMPLOYEE_SCREEN_TITLE else "${selectedItems.size} Selected", - placeholderText = EMPLOYEE_SEARCH_PLACEHOLDER, - showSearchBar = showSearchBar, - selectionCount = selectedItems.size, - searchText = searchText, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyListState.isScrolled, @@ -147,19 +148,32 @@ fun EmployeeScreen( } ) }, - fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - navController.navigate(AddEditEmployeeScreenDestination(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true + navActions = { + ScaffoldNavActions( + placeholderText = EMPLOYEE_SEARCH_PLACEHOLDER, + showSettingsIcon = true, + selectionCount = selectedItems.size, + showSearchIcon = showSearchBar, + searchText = searchText, + onEditClick = { + navController.navigate(AddEditEmployeeScreenDestination(selectedItems.first())) + }, + onDeleteClick = { + openDialog.value = true + }, + onSettingsClick = {}, + onSelectAllClick = viewModel::selectAllItems, + onClearClick = viewModel::clearSearchText, + onSearchClick = viewModel::openSearchBar, + onSearchTextChanged = viewModel::searchTextChanged + ) }, + fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, - onSearchTextChanged = viewModel::searchTextChanged, - onSearchClick = viewModel::openSearchBar, onBackClick = viewModel::closeSearchBar, - onClearClick = viewModel::clearSearchText + snackbarHostState = snackbarState, ) { _ -> when (state) { is UiState.Loading -> LoadingIndicator() @@ -290,6 +304,9 @@ fun EmployeeData( Icons.Filled.ArrowRight, contentDescription = "Localized description", ) - } + }, + colors = ListItemDefaults.colors( + containerColor = LightColor9 + ) ) } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/EmployeeViewModel.kt b/feature/employee/src/main/java/com/niyaj/employee/EmployeeViewModel.kt similarity index 77% rename from app/src/main/java/com/niyaj/poposroom/features/employee/presentation/EmployeeViewModel.kt rename to feature/employee/src/main/java/com/niyaj/employee/EmployeeViewModel.kt index 89c6a7d0..d6b39ebb 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/EmployeeViewModel.kt +++ b/feature/employee/src/main/java/com/niyaj/employee/EmployeeViewModel.kt @@ -1,14 +1,14 @@ -package com.niyaj.poposroom.features.employee.presentation +package com.niyaj.employee import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.employee.domain.repository.EmployeeRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.EmployeeRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -23,7 +23,8 @@ import javax.inject.Inject @HiltViewModel class EmployeeViewModel @Inject constructor( private val employeeRepository: EmployeeRepository, - @Dispatcher(PoposDispatchers.IO) private val ioDispatcher: CoroutineDispatcher + @Dispatcher(PoposDispatchers.IO) + private val ioDispatcher: CoroutineDispatcher ): BaseViewModel() { override var totalItems: List = emptyList() diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeEvent.kt b/feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeEvent.kt similarity index 80% rename from app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeEvent.kt rename to feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeEvent.kt index 4ddf789e..8d46f792 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeEvent.kt +++ b/feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeEvent.kt @@ -1,7 +1,7 @@ -package com.niyaj.poposroom.features.employee.presentation.add_edit +package com.niyaj.employee.add_edit -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeSalaryType -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeType +import com.niyaj.model.EmployeeSalaryType +import com.niyaj.model.EmployeeType sealed interface AddEditEmployeeEvent { @@ -25,7 +25,8 @@ sealed interface AddEditEmployeeEvent { data class EmployeeEmailChanged(val employeeEmail: String) : AddEditEmployeeEvent - data class EmployeeSalaryTypeChanged(val employeeSalaryType: EmployeeSalaryType) : AddEditEmployeeEvent + data class EmployeeSalaryTypeChanged(val employeeSalaryType: EmployeeSalaryType) : + AddEditEmployeeEvent data class EmployeePositionChanged(val employeePosition: String) : AddEditEmployeeEvent diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeScreen.kt b/feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeScreen.kt similarity index 80% rename from app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeScreen.kt rename to feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeScreen.kt index 8ddc478f..b3d34c29 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeScreen.kt +++ b/feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee.presentation.add_edit +package com.niyaj.employee.add_edit import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -9,8 +9,6 @@ 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.Divider -import androidx.compose.material.IconButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ArrowForward @@ -23,6 +21,7 @@ import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.PersonPin import androidx.compose.material.icons.filled.PhoneAndroid import androidx.compose.material.icons.filled.Radar +import androidx.compose.material3.Divider import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ElevatedFilterChip @@ -31,6 +30,7 @@ import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.FilterChipDefaults 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 @@ -52,33 +52,35 @@ import androidx.compose.ui.unit.toSize import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardOutlinedTextField -import com.niyaj.poposroom.features.common.components.StandardScaffoldWithOutDrawer -import com.niyaj.poposroom.features.common.components.TextWithIcon -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.toJoinedDate -import com.niyaj.poposroom.features.common.utils.toMilliSecond -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeSalaryType -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.ADD_EDIT_EMPLOYEE_BUTTON -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.CREATE_NEW_EMPLOYEE -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EDIT_EMPLOYEE -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_EMAIL_FIELD -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_JOINED_DATE_FIELD -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_NAME_ERROR -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_NAME_FIELD -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_PHONE_ERROR -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_PHONE_FIELD -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_POSITION_ERROR -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_POSITION_FIELD -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_SALARY_ERROR -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_SALARY_FIELD -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_SALARY_TYPE_FIELD -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeTestTags.EMPLOYEE_TYPE_FIELD -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeType +import com.niyaj.common.utils.toJoinedDate +import com.niyaj.common.utils.toMilliSecond +import com.niyaj.data.utils.EmployeeTestTags.ADD_EDIT_EMPLOYEE_BUTTON +import com.niyaj.data.utils.EmployeeTestTags.CREATE_NEW_EMPLOYEE +import com.niyaj.data.utils.EmployeeTestTags.EDIT_EMPLOYEE +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_EMAIL_FIELD +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_JOINED_DATE_FIELD +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_NAME_ERROR +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_NAME_FIELD +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_PHONE_ERROR +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_PHONE_FIELD +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_POSITION_ERROR +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_POSITION_FIELD +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_SALARY_ERROR +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_SALARY_FIELD +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_SALARY_TYPE_FIELD +import com.niyaj.data.utils.EmployeeTestTags.EMPLOYEE_TYPE_FIELD +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.model.EmployeeSalaryType +import com.niyaj.model.EmployeeType +import com.niyaj.ui.components.PhoneNoCountBox +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.components.TextWithIcon +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.result.ResultBackNavigator import com.vanpra.composematerialdialogs.MaterialDialog @@ -88,12 +90,14 @@ import java.time.LocalDate @OptIn(ExperimentalMaterial3Api::class) @Composable -@Destination +@Destination( + route = Screens.AddEditEmployeeScreen +) fun AddEditEmployeeScreen( employeeId: Int = 0, navController: NavController, viewModel: AddEditEmployeeViewModel = hiltViewModel(), - resultBackNavigator: ResultBackNavigator + resultBackNavigator: ResultBackNavigator, ) { val phoneError = viewModel.phoneError.collectAsStateWithLifecycle().value val nameError = viewModel.nameError.collectAsStateWithLifecycle().value @@ -108,11 +112,11 @@ fun AddEditEmployeeScreen( LaunchedEffect(key1 = event) { event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} + when (data) { is UiEvent.OnError -> { resultBackNavigator.navigateBack(data.errorMessage) } + is UiEvent.OnSuccess -> { resultBackNavigator.navigateBack(data.successMessage) } @@ -153,7 +157,7 @@ fun AddEditEmployeeScreen( modifier = Modifier .fillMaxWidth() .padding(SpaceSmall), - verticalArrangement = Arrangement.Center, + verticalArrangement = Arrangement.spacedBy(SpaceSmall), ) { item(EMPLOYEE_NAME_FIELD) { StandardOutlinedTextField( @@ -167,7 +171,6 @@ fun AddEditEmployeeScreen( viewModel.onEvent(AddEditEmployeeEvent.EmployeeNameChanged(it)) } ) - Spacer(modifier = Modifier.height(SpaceSmall)) } item(EMPLOYEE_PHONE_FIELD) { @@ -179,12 +182,15 @@ fun AddEditEmployeeScreen( errorText = phoneError, errorTextTag = EMPLOYEE_PHONE_ERROR, keyboardType = KeyboardType.Number, + trailingIcon = { + PhoneNoCountBox( + count = viewModel.state.employeePhone.length + ) + }, onValueChange = { viewModel.onEvent(AddEditEmployeeEvent.EmployeePhoneChanged(it)) } ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(EMPLOYEE_SALARY_FIELD) { @@ -200,8 +206,6 @@ fun AddEditEmployeeScreen( viewModel.onEvent(AddEditEmployeeEvent.EmployeeSalaryChanged(it)) } ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(EMPLOYEE_POSITION_FIELD) { @@ -233,10 +237,10 @@ fun AddEditEmployeeScreen( }, ) - if(positions.isNotEmpty()) { + if (positions.isNotEmpty()) { DropdownMenu( modifier = Modifier - .width(with(LocalDensity.current){textFieldSize.width.toDp()}), + .width(with(LocalDensity.current) { textFieldSize.width.toDp() }), expanded = expanded, onDismissRequest = { expanded = false }, ) { @@ -255,8 +259,12 @@ fun AddEditEmployeeScreen( contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, ) - if(index != positions.size - 1) { - Divider(modifier = Modifier.fillMaxWidth(), color = Color.Gray, thickness = 0.8.dp) + if (index != positions.size - 1) { + Divider( + modifier = Modifier.fillMaxWidth(), + color = Color.Gray, + thickness = 0.8.dp + ) } } } @@ -275,8 +283,6 @@ fun AddEditEmployeeScreen( viewModel.onEvent(AddEditEmployeeEvent.EmployeeEmailChanged(it)) } ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(EMPLOYEE_JOINED_DATE_FIELD) { @@ -288,7 +294,10 @@ fun AddEditEmployeeScreen( IconButton( onClick = { dialogState.show() } ) { - Icon(imageVector = Icons.Default.CalendarMonth, contentDescription = "Choose a date") + Icon( + imageVector = Icons.Default.CalendarMonth, + contentDescription = "Choose a date" + ) } }, readOnly = true, @@ -303,8 +312,6 @@ fun AddEditEmployeeScreen( } } ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(EMPLOYEE_TYPE_FIELD) { @@ -337,11 +344,9 @@ fun AddEditEmployeeScreen( } } } - - Spacer(modifier = Modifier.height(SpaceSmall)) } - item(EMPLOYEE_SALARY_TYPE_FIELD){ + item(EMPLOYEE_SALARY_TYPE_FIELD) { Column( modifier = Modifier .fillMaxWidth() @@ -379,8 +384,6 @@ fun AddEditEmployeeScreen( } } } - - Spacer(modifier = Modifier.height(SpaceSmall)) } } } @@ -396,7 +399,7 @@ fun AddEditEmployeeScreen( allowedDateValidator = { date -> date <= LocalDate.now() } - ) {date -> + ) { date -> viewModel.onEvent(AddEditEmployeeEvent.EmployeeJoinedDateChanged(date.toMilliSecond)) } } diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeState.kt b/feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeState.kt similarity index 68% rename from app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeState.kt rename to feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeState.kt index 21ad234d..d2dc4b27 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeState.kt +++ b/feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeState.kt @@ -1,8 +1,8 @@ -package com.niyaj.poposroom.features.employee.presentation.add_edit +package com.niyaj.employee.add_edit -import com.niyaj.poposroom.features.common.utils.toMilliSecond -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeSalaryType -import com.niyaj.poposroom.features.employee.domain.utils.EmployeeType +import com.niyaj.common.utils.toMilliSecond +import com.niyaj.model.EmployeeSalaryType +import com.niyaj.model.EmployeeType import java.time.LocalDate data class AddEditEmployeeState( diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeViewModel.kt b/feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeViewModel.kt similarity index 94% rename from app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeViewModel.kt rename to feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeViewModel.kt index 2bfb6d0d..3dcd5379 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee/presentation/add_edit/AddEditEmployeeViewModel.kt +++ b/feature/employee/src/main/java/com/niyaj/employee/add_edit/AddEditEmployeeViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee.presentation.add_edit +package com.niyaj.employee.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,11 +7,11 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee.domain.repository.EmployeeRepository -import com.niyaj.poposroom.features.employee.domain.repository.EmployeeValidationRepository +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.EmployeeRepository +import com.niyaj.data.repository.EmployeeValidationRepository +import com.niyaj.model.Employee +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow diff --git a/feature/employee/src/test/java/com/niyaj/employee/ExampleUnitTest.kt b/feature/employee/src/test/java/com/niyaj/employee/ExampleUnitTest.kt new file mode 100644 index 00000000..faeea7c1 --- /dev/null +++ b/feature/employee/src/test/java/com/niyaj/employee/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.employee + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/employee_absent/.gitignore b/feature/employee_absent/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/employee_absent/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/employee_absent/build.gradle.kts b/feature/employee_absent/build.gradle.kts new file mode 100644 index 00000000..42b41bd2 --- /dev/null +++ b/feature/employee_absent/build.gradle.kts @@ -0,0 +1,32 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.employee_absent" + + ksp { + arg("compose-destinations.moduleName", "employee_absent") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + implementation(libs.dialog.core) + implementation(libs.dialog.datetime) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/employee_absent/proguard-rules.pro b/feature/employee_absent/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/employee_absent/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/employee_absent/src/androidTest/java/com/niyaj/employee_absent/ExampleInstrumentedTest.kt b/feature/employee_absent/src/androidTest/java/com/niyaj/employee_absent/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..2dcaf0bf --- /dev/null +++ b/feature/employee_absent/src/androidTest/java/com/niyaj/employee_absent/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.employee_absent + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.employee_absent", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/employee_absent/src/main/AndroidManifest.xml b/feature/employee_absent/src/main/AndroidManifest.xml new file mode 100644 index 00000000..44008a43 --- /dev/null +++ b/feature/employee_absent/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/AbsentScreen.kt b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/AbsentScreen.kt similarity index 65% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/AbsentScreen.kt rename to feature/employee_absent/src/main/java/com/niyaj/employee_absent/AbsentScreen.kt index 22c33a77..9422fbbf 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/AbsentScreen.kt +++ b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/AbsentScreen.kt @@ -1,6 +1,7 @@ -package com.niyaj.poposroom.features.employee_absent.presentation +package com.niyaj.employee_absent import androidx.activity.compose.BackHandler +import androidx.compose.animation.Crossfade import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi @@ -38,49 +39,55 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardAssistChip -import com.niyaj.poposroom.features.common.components.StandardElevatedCard -import com.niyaj.poposroom.features.common.components.StandardExpandable -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.components.TextWithBorderCount -import com.niyaj.poposroom.features.common.components.TextWithIcon -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.common.utils.toDate -import com.niyaj.poposroom.features.common.utils.toMonthAndYear -import com.niyaj.poposroom.features.destinations.AddEditAbsentScreenDestination -import com.niyaj.poposroom.features.employee_absent.domain.model.Absent -import com.niyaj.poposroom.features.employee_absent.domain.model.EmployeeWithAbsent -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_NOT_AVAIlABLE -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_SCREEN_TITLE -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_TAG -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.CREATE_NEW_ABSENT -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.DELETE_ABSENT_MESSAGE -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.DELETE_ABSENT_TITLE -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.NO_ITEMS_IN_ABSENT +import com.niyaj.common.utils.toDate +import com.niyaj.common.utils.toMonthAndYear +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_NOT_AVAIlABLE +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_SCREEN_TITLE +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_TAG +import com.niyaj.data.utils.AbsentScreenTags.CREATE_NEW_ABSENT +import com.niyaj.data.utils.AbsentScreenTags.DELETE_ABSENT_MESSAGE +import com.niyaj.data.utils.AbsentScreenTags.DELETE_ABSENT_TITLE +import com.niyaj.data.utils.AbsentScreenTags.NO_ITEMS_IN_ABSENT +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.employee_absent.destinations.AddEditAbsentScreenDestination +import com.niyaj.model.Absent +import com.niyaj.model.EmployeeWithAbsents +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardAssistChip +import com.niyaj.ui.components.StandardElevatedCard +import com.niyaj.ui.components.StandardExpandable +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.components.TextWithBorderCount +import com.niyaj.ui.components.TextWithIcon +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.navigate import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.AbsentScreen +) @Composable fun AbsentScreen( navController: NavController, viewModel: AbsentViewModel = hiltViewModel(), - resultRecipient: ResultRecipient + resultRecipient: ResultRecipient, ) { val scope = rememberCoroutineScope() val snackbarState = remember { SnackbarHostState() } - val state = viewModel.absents.collectAsStateWithLifecycle().value + val uiState = viewModel.absents.collectAsStateWithLifecycle().value val selectedItems = viewModel.selectedItems.toList() @@ -99,13 +106,13 @@ fun AbsentScreen( LaunchedEffect(key1 = event) { event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} + when (data) { is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) } } + is UiEvent.OnSuccess -> { scope.launch { snackbarState.showSnackbar(data.successMessage) @@ -123,7 +130,7 @@ fun AbsentScreen( } } - resultRecipient.onNavResult {result -> + resultRecipient.onNavResult { result -> when (result) { is NavResult.Canceled -> {} is NavResult.Value -> { @@ -138,13 +145,7 @@ fun AbsentScreen( StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) ABSENT_SCREEN_TITLE else "${selectedItems.size} Selected", - placeholderText = ABSENT_SEARCH_PLACEHOLDER, - showSearchBar = showSearchBar, - selectionCount = selectedItems.size, - searchText = searchText, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyListState.isScrolled, @@ -161,63 +162,82 @@ fun AbsentScreen( } ) }, - fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - navController.navigate(AddEditAbsentScreenDestination(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true + navActions = { + ScaffoldNavActions( + placeholderText = ABSENT_SEARCH_PLACEHOLDER, + showSettingsIcon = true, + selectionCount = selectedItems.size, + showSearchIcon = showSearchBar, + searchText = searchText, + onEditClick = { + navController.navigate(AddEditAbsentScreenDestination(selectedItems.first())) + }, + onDeleteClick = { + openDialog.value = true + }, + onSettingsClick = {}, + onSelectAllClick = viewModel::selectAllItems, + onClearClick = viewModel::clearSearchText, + onSearchClick = viewModel::openSearchBar, + onSearchTextChanged = viewModel::searchTextChanged + ) }, + fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, - onSearchTextChanged = viewModel::searchTextChanged, - onSearchClick = viewModel::openSearchBar, onBackClick = viewModel::closeSearchBar, - onClearClick = viewModel::clearSearchText, + snackbarHostState = snackbarState, ) { _ -> - when (state) { - is UiState.Loading -> LoadingIndicator() - is UiState.Empty -> { - ItemNotAvailable( - text = if (searchText.isEmpty()) ABSENT_NOT_AVAIlABLE else NO_ITEMS_IN_ABSENT, - buttonText = CREATE_NEW_ABSENT, - onClick = { - navController.navigate(AddEditAbsentScreenDestination()) - } - ) - } - is UiState.Success -> { - LazyColumn( - modifier = Modifier - .padding(SpaceSmall), - state = lazyListState - ) { - items( - items = state.data, - key = { it.employee.employeeId } - ) { item -> - if (item.absents.isNotEmpty()) { - AbsentData( - item = item, - expanded = { - selectedEmployee == it - }, - doesSelected = { - selectedItems.contains(it) - }, - onClick = { - if (selectedItems.isNotEmpty()) { - viewModel.selectItem(it) + Crossfade( + targetState = uiState, + label = "Absent State" + ) { state -> + when (state) { + is UiState.Loading -> LoadingIndicator() + is UiState.Empty -> { + ItemNotAvailable( + text = if (searchText.isEmpty()) ABSENT_NOT_AVAIlABLE else NO_ITEMS_IN_ABSENT, + buttonText = CREATE_NEW_ABSENT, + onClick = { + navController.navigate(AddEditAbsentScreenDestination()) + } + ) + } + + is UiState.Success -> { + LazyColumn( + modifier = Modifier + .padding(SpaceSmall), + state = lazyListState + ) { + items( + items = state.data, + key = { it.employee.employeeId } + ) { item -> + if (item.absents.isNotEmpty()) { + AbsentData( + item = item, + expanded = { + selectedEmployee == it + }, + doesSelected = { + selectedItems.contains(it) + }, + onClick = { + if (selectedItems.isNotEmpty()) { + viewModel.selectItem(it) + } + }, + onExpandChanged = viewModel::selectEmployee, + onLongClick = viewModel::selectItem, + onChipClick = { +// navController.navigate(AddEditAbsentScreenDestination(employeeId = it)) } - }, - onExpandChanged = viewModel::selectEmployee, - onLongClick = viewModel::selectItem, - onChipClick = { - navController.navigate(AddEditAbsentScreenDestination(employeeId = it)) - } - ) + ) - Spacer(modifier = Modifier.height(SpaceSmall)) + Spacer(modifier = Modifier.height(SpaceSmall)) + } } } } @@ -267,8 +287,8 @@ fun AbsentScreen( @Composable fun AbsentData( modifier: Modifier = Modifier, - item: EmployeeWithAbsent, - expanded: (Int) -> Boolean, + item: EmployeeWithAbsents, + expanded: (Int) -> Boolean, onExpandChanged: (Int) -> Unit, doesSelected: (Int) -> Boolean, onClick: (Int) -> Unit, @@ -326,7 +346,7 @@ fun AbsentData( @Composable fun EmployeeAbsentData( modifier: Modifier = Modifier, - groupedAbsents: Map>, + groupedAbsents: Map>, doesSelected: (Int) -> Boolean, onClick: (Int) -> Unit, onLongClick: (Int) -> Unit, @@ -349,7 +369,7 @@ fun EmployeeAbsentData( .padding(SpaceMini), horizontalArrangement = Arrangement.Start ) { - grouped.value.forEach{ item -> + grouped.value.forEach { item -> StandardElevatedCard( modifier = modifier, containerColor = MaterialTheme.colorScheme.onPrimary, diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/AbsentViewModel.kt b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/AbsentViewModel.kt similarity index 83% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/AbsentViewModel.kt rename to feature/employee_absent/src/main/java/com/niyaj/employee_absent/AbsentViewModel.kt index 008e50bc..56525b34 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/AbsentViewModel.kt +++ b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/AbsentViewModel.kt @@ -1,14 +1,14 @@ -package com.niyaj.poposroom.features.employee_absent.presentation +package com.niyaj.employee_absent import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.employee_absent.domain.repository.AbsentRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.AbsentRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentEvent.kt b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentEvent.kt similarity index 71% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentEvent.kt rename to feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentEvent.kt index 465c9c24..220b5e01 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentEvent.kt +++ b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentEvent.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.employee_absent.presentation.add_edit +package com.niyaj.employee_absent.add_edit -import com.niyaj.poposroom.features.employee.domain.model.Employee +import com.niyaj.model.Employee sealed interface AddEditAbsentEvent { diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentScreen.kt b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentScreen.kt similarity index 85% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentScreen.kt rename to feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentScreen.kt index 5fc706f5..884804e3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentScreen.kt +++ b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_absent.presentation.add_edit +package com.niyaj.employee_absent.add_edit import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -16,6 +16,7 @@ import androidx.compose.material.icons.filled.CalendarMonth import androidx.compose.material.icons.filled.CalendarToday import androidx.compose.material.icons.filled.Description import androidx.compose.material.icons.filled.EditCalendar +import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Person4 import androidx.compose.material3.Divider import androidx.compose.material3.DropdownMenu @@ -36,7 +37,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag @@ -46,27 +46,27 @@ import androidx.compose.ui.unit.toSize import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardOutlinedTextField -import com.niyaj.poposroom.features.common.components.StandardScaffoldWithOutDrawer -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.toMilliSecond -import com.niyaj.poposroom.features.common.utils.toPrettyDate -import com.niyaj.poposroom.features.destinations.AddEditEmployeeScreenDestination -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_DATE_ERROR -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_DATE_FIELD -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_EMPLOYEE_NAME_ERROR -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_EMPLOYEE_NAME_FIELD -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ABSENT_REASON_FIELD -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ADD_EDIT_ABSENT_ENTRY_BUTTON -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.ADD_EDIT_ABSENT_SCREEN -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.CREATE_NEW_ABSENT -import com.niyaj.poposroom.features.employee_absent.domain.utils.AbsentScreenTags.EDIT_ABSENT_ITEM +import com.niyaj.common.utils.toMilliSecond +import com.niyaj.common.utils.toPrettyDate +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_DATE_ERROR +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_DATE_FIELD +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_EMPLOYEE_NAME_ERROR +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_EMPLOYEE_NAME_FIELD +import com.niyaj.data.utils.AbsentScreenTags.ABSENT_REASON_FIELD +import com.niyaj.data.utils.AbsentScreenTags.ADD_EDIT_ABSENT_ENTRY_BUTTON +import com.niyaj.data.utils.AbsentScreenTags.ADD_EDIT_ABSENT_SCREEN +import com.niyaj.data.utils.AbsentScreenTags.CREATE_NEW_ABSENT +import com.niyaj.data.utils.AbsentScreenTags.EDIT_ABSENT_ITEM +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.navigation.navigate import com.ramcosta.composedestinations.result.ResultBackNavigator import com.vanpra.composematerialdialogs.MaterialDialog import com.vanpra.composematerialdialogs.datetime.date.datepicker @@ -74,7 +74,9 @@ import com.vanpra.composematerialdialogs.rememberMaterialDialogState import java.time.LocalDate @OptIn(ExperimentalMaterial3Api::class) -@Destination +@Destination( + route = Screens.AddEditAbsentScreen +) @Composable fun AddEditAbsentScreen( absentId: Int = 0, @@ -97,7 +99,6 @@ fun AddEditAbsentScreen( LaunchedEffect(key1 = event) { event?.let { data -> when(data) { - is UiEvent.IsLoading -> {} is UiEvent.OnError -> { resultBackNavigator.navigateBack(data.errorMessage) } @@ -195,11 +196,22 @@ fun AddEditAbsentScreen( }, text = { Text(text = employee.employeeName) + }, + leadingIcon = { + CircularBox( + icon = Icons.Default.Person, + doesSelected = false, + size = 30.dp, + showBorder = false, + text = employee.employeeName + ) } ) if(index != employees.size - 1) { - Divider(modifier = Modifier.fillMaxWidth(), color = Color.Gray, thickness = 0.8.dp) + Divider(modifier = Modifier + .fillMaxWidth() + .padding(start = 44.dp)) } } @@ -228,7 +240,7 @@ fun AddEditAbsentScreen( modifier = Modifier .fillMaxWidth(), onClick = { - navController.navigate(AddEditEmployeeScreenDestination()) + navController.navigate(Screens.AddEditEmployeeScreen) }, text = { Text( @@ -251,8 +263,6 @@ fun AddEditAbsentScreen( } ) } - - } Spacer(modifier = Modifier.height(SpaceSmall)) diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentState.kt b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentState.kt similarity index 53% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentState.kt rename to feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentState.kt index 74c98624..8ab2bba1 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentState.kt +++ b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentState.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.employee_absent.presentation.add_edit +package com.niyaj.employee_absent.add_edit -import com.niyaj.poposroom.features.common.utils.toMilliSecond +import com.niyaj.common.utils.toMilliSecond import java.time.LocalDate data class AddEditAbsentState( diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentViewModel.kt b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentViewModel.kt similarity index 87% rename from app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentViewModel.kt rename to feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentViewModel.kt index 94e14166..19cdc0b6 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_absent/presentation/add_edit/AddEditAbsentViewModel.kt +++ b/feature/employee_absent/src/main/java/com/niyaj/employee_absent/add_edit/AddEditAbsentViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_absent.presentation.add_edit +package com.niyaj.employee_absent.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,12 +7,12 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee_absent.domain.model.Absent -import com.niyaj.poposroom.features.employee_absent.domain.repository.AbsentRepository -import com.niyaj.poposroom.features.employee_absent.domain.repository.AbsentValidationRepository +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.AbsentRepository +import com.niyaj.data.repository.validation.AbsentValidationRepository +import com.niyaj.model.Absent +import com.niyaj.model.Employee +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow @@ -33,10 +33,10 @@ import javax.inject.Inject class AddEditAbsentViewModel @Inject constructor( private val absentRepository: AbsentRepository, private val validationRepository: AbsentValidationRepository, - savedStateHandle: SavedStateHandle + savedStateHandle: SavedStateHandle, ) : ViewModel() { - val absentId = savedStateHandle.get("absentId") + private val absentId = savedStateHandle.get("absentId") var state by mutableStateOf(AddEditAbsentState()) @@ -110,10 +110,11 @@ class AddEditAbsentViewModel @Inject constructor( private fun getAbsentById(itemId: Int) { viewModelScope.launch { - when(val result = absentRepository.getAbsentById(itemId)) { + when (val result = absentRepository.getAbsentById(itemId)) { is Resource.Error -> { _eventFlow.emit(UiEvent.OnError("Unable to find employee absent")) } + is Resource.Success -> { result.data?.let { absent -> getEmployeeById(absent.employeeId) @@ -150,7 +151,7 @@ class AddEditAbsentViewModel @Inject constructor( updatedAt = if (absentId == 0) null else Date() ) - when(absentRepository.upsertAbsent(newAbsent)) { + when (absentRepository.upsertAbsent(newAbsent)) { is Resource.Error -> { _eventFlow.emit(UiEvent.OnError("Unable To Mark Employee Absent.")) } diff --git a/feature/employee_absent/src/test/java/com/niyaj/employee_absent/ExampleUnitTest.kt b/feature/employee_absent/src/test/java/com/niyaj/employee_absent/ExampleUnitTest.kt new file mode 100644 index 00000000..8764e974 --- /dev/null +++ b/feature/employee_absent/src/test/java/com/niyaj/employee_absent/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.employee_absent + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/employee_payment/.gitignore b/feature/employee_payment/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/employee_payment/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/employee_payment/build.gradle.kts b/feature/employee_payment/build.gradle.kts new file mode 100644 index 00000000..740e5560 --- /dev/null +++ b/feature/employee_payment/build.gradle.kts @@ -0,0 +1,32 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.employee_payment" + + ksp { + arg("compose-destinations.moduleName", "employee_payment") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + implementation(libs.dialog.core) + implementation(libs.dialog.datetime) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/employee_payment/proguard-rules.pro b/feature/employee_payment/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/employee_payment/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/employee_payment/src/androidTest/java/com/niyaj/employee_payment/ExampleInstrumentedTest.kt b/feature/employee_payment/src/androidTest/java/com/niyaj/employee_payment/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..243ec1d7 --- /dev/null +++ b/feature/employee_payment/src/androidTest/java/com/niyaj/employee_payment/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.employee_payment + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.employee_payment", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/employee_payment/src/main/AndroidManifest.xml b/feature/employee_payment/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/employee_payment/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/PaymentScreen.kt b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/PaymentScreen.kt similarity index 82% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/PaymentScreen.kt rename to feature/employee_payment/src/main/java/com/niyaj/employee_payment/PaymentScreen.kt index c83849e4..c90e1bf7 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/PaymentScreen.kt +++ b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/PaymentScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_payment.presentation +package com.niyaj.employee_payment import androidx.activity.compose.BackHandler import androidx.compose.foundation.BorderStroke @@ -16,7 +16,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MergeType import androidx.compose.material.icons.filled.Money @@ -27,6 +26,7 @@ import androidx.compose.material3.AssistChip import androidx.compose.material3.AssistChipDefaults import androidx.compose.material3.ElevatedAssistChip import androidx.compose.material3.FabPosition +import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme @@ -49,38 +49,44 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.components.TextWithIcon -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.IconSizeSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.common.utils.toPrettyDate -import com.niyaj.poposroom.features.common.utils.toRupee -import com.niyaj.poposroom.features.destinations.AddEditPaymentScreenDestination -import com.niyaj.poposroom.features.employee_payment.domain.model.Payment -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.CREATE_NEW_PAYMENT -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.DELETE_PAYMENT_MESSAGE -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.DELETE_PAYMENT_TITLE -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.NO_ITEMS_IN_PAYMENT -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_NOT_AVAIlABLE -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_SCREEN_TITLE -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_TAG +import com.niyaj.common.utils.toPrettyDate +import com.niyaj.common.utils.toRupee +import com.niyaj.data.utils.PaymentScreenTags.CREATE_NEW_PAYMENT +import com.niyaj.data.utils.PaymentScreenTags.DELETE_PAYMENT_MESSAGE +import com.niyaj.data.utils.PaymentScreenTags.DELETE_PAYMENT_TITLE +import com.niyaj.data.utils.PaymentScreenTags.NO_ITEMS_IN_PAYMENT +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_NOT_AVAIlABLE +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_SCREEN_TITLE +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_TAG +import com.niyaj.designsystem.theme.IconSizeSmall +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.employee_payment.destinations.AddEditPaymentScreenDestination +import com.niyaj.model.Payment +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.components.TextWithIcon +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.navigate import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch @OptIn(ExperimentalFoundationApi::class) -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.PaymentScreen +) @Composable fun PaymentScreen( navController: NavController, @@ -110,7 +116,6 @@ fun PaymentScreen( LaunchedEffect(key1 = event) { event?.let { data -> when(data) { - is UiEvent.IsLoading -> {} is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) @@ -148,13 +153,7 @@ fun PaymentScreen( StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) PAYMENT_SCREEN_TITLE else "${selectedItems.size} Selected", - placeholderText = PAYMENT_SEARCH_PLACEHOLDER, - showSearchBar = showSearchBar, - selectionCount = selectedItems.size, - searchText = searchText, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyListState.isScrolled, @@ -171,19 +170,32 @@ fun PaymentScreen( } ) }, - fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - navController.navigate(AddEditPaymentScreenDestination(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true + navActions = { + ScaffoldNavActions( + placeholderText = PAYMENT_SEARCH_PLACEHOLDER, + showSettingsIcon = true, + selectionCount = selectedItems.size, + showSearchIcon = showSearchBar, + searchText = searchText, + onEditClick = { + navController.navigate(AddEditPaymentScreenDestination(selectedItems.first())) + }, + onDeleteClick = { + openDialog.value = true + }, + onSettingsClick = {}, + onSelectAllClick = viewModel::selectAllItems, + onClearClick = viewModel::clearSearchText, + onSearchClick = viewModel::openSearchBar, + onSearchTextChanged = viewModel::searchTextChanged + ) }, + fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, - onSearchTextChanged = viewModel::searchTextChanged, - onSearchClick = viewModel::openSearchBar, onBackClick = viewModel::closeSearchBar, - onClearClick = viewModel::clearSearchText, + snackbarHostState = snackbarState, ) { _ -> when (state) { is UiState.Loading -> LoadingIndicator() diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/PaymentViewModel.kt b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/PaymentViewModel.kt similarity index 76% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/PaymentViewModel.kt rename to feature/employee_payment/src/main/java/com/niyaj/employee_payment/PaymentViewModel.kt index 389e313e..d6591442 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/PaymentViewModel.kt +++ b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/PaymentViewModel.kt @@ -1,14 +1,14 @@ -package com.niyaj.poposroom.features.employee_payment.presentation +package com.niyaj.employee_payment import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.employee_payment.domain.repository.PaymentRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.PaymentRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -23,7 +23,8 @@ import javax.inject.Inject @HiltViewModel class PaymentViewModel @Inject constructor( private val paymentRepository: PaymentRepository, - @Dispatcher(PoposDispatchers.IO) private val ioDispatcher: CoroutineDispatcher + @Dispatcher(PoposDispatchers.IO) + private val ioDispatcher: CoroutineDispatcher ): BaseViewModel() { override var totalItems: List = emptyList() diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentEvent.kt b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentEvent.kt similarity index 69% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentEvent.kt rename to feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentEvent.kt index 32806c89..820f5a39 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentEvent.kt +++ b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentEvent.kt @@ -1,8 +1,8 @@ -package com.niyaj.poposroom.features.employee_payment.presentation.add_edit +package com.niyaj.employee_payment.add_edit -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee.domain.utils.PaymentMode -import com.niyaj.poposroom.features.employee.domain.utils.PaymentType +import com.niyaj.model.Employee +import com.niyaj.model.PaymentMode +import com.niyaj.model.PaymentType sealed interface AddEditPaymentEvent { diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentScreen.kt b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentScreen.kt similarity index 85% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentScreen.kt rename to feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentScreen.kt index 74de2b4f..f561e2f4 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentScreen.kt +++ b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_payment.presentation.add_edit +package com.niyaj.employee_payment.add_edit import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -19,6 +19,7 @@ import androidx.compose.material.icons.filled.Description import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.MergeType import androidx.compose.material.icons.filled.Money +import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.Person4 import androidx.compose.material3.Divider import androidx.compose.material3.DropdownMenu @@ -41,7 +42,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.testTag @@ -52,35 +52,35 @@ import androidx.compose.ui.unit.toSize import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardOutlinedTextField -import com.niyaj.poposroom.features.common.components.StandardScaffoldWithOutDrawer -import com.niyaj.poposroom.features.common.components.TextWithIcon -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.toMilliSecond -import com.niyaj.poposroom.features.common.utils.toPrettyDate -import com.niyaj.poposroom.features.destinations.AddEditEmployeeScreenDestination -import com.niyaj.poposroom.features.employee.domain.utils.PaymentMode -import com.niyaj.poposroom.features.employee.domain.utils.PaymentType -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.ADD_EDIT_PAYMENT_ENTRY_BUTTON -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.ADD_EDIT_PAYMENT_SCREEN -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.CREATE_NEW_PAYMENT -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.EDIT_PAYMENT_ITEM -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.GIVEN_AMOUNT_ERROR -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.GIVEN_AMOUNT_FIELD -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.GIVEN_DATE_ERROR -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.GIVEN_DATE_FIELD -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_EMPLOYEE_NAME_ERROR -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_EMPLOYEE_NAME_FIELD -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_MODE_FIELD -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_NOTE_ERROR -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_NOTE_FIELD -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags.PAYMENT_TYPE_FIELD +import com.niyaj.common.utils.toMilliSecond +import com.niyaj.common.utils.toPrettyDate +import com.niyaj.data.utils.PaymentScreenTags.ADD_EDIT_PAYMENT_ENTRY_BUTTON +import com.niyaj.data.utils.PaymentScreenTags.ADD_EDIT_PAYMENT_SCREEN +import com.niyaj.data.utils.PaymentScreenTags.CREATE_NEW_PAYMENT +import com.niyaj.data.utils.PaymentScreenTags.EDIT_PAYMENT_ITEM +import com.niyaj.data.utils.PaymentScreenTags.GIVEN_AMOUNT_ERROR +import com.niyaj.data.utils.PaymentScreenTags.GIVEN_AMOUNT_FIELD +import com.niyaj.data.utils.PaymentScreenTags.GIVEN_DATE_ERROR +import com.niyaj.data.utils.PaymentScreenTags.GIVEN_DATE_FIELD +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_EMPLOYEE_NAME_ERROR +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_EMPLOYEE_NAME_FIELD +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_MODE_FIELD +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_NOTE_ERROR +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_NOTE_FIELD +import com.niyaj.data.utils.PaymentScreenTags.PAYMENT_TYPE_FIELD +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.model.PaymentMode +import com.niyaj.model.PaymentType +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.components.TextWithIcon +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.navigation.navigate import com.ramcosta.composedestinations.result.ResultBackNavigator import com.vanpra.composematerialdialogs.MaterialDialog import com.vanpra.composematerialdialogs.datetime.date.datepicker @@ -88,7 +88,9 @@ import com.vanpra.composematerialdialogs.rememberMaterialDialogState import java.time.LocalDate @OptIn(ExperimentalMaterial3Api::class) -@Destination +@Destination( + route = Screens.AddEditPaymentScreen +) @Composable fun AddEditPaymentScreen( paymentId: Int = 0, @@ -122,7 +124,6 @@ fun AddEditPaymentScreen( LaunchedEffect(key1 = event) { event?.let { data -> when(data) { - is UiEvent.IsLoading -> {} is UiEvent.OnError -> { resultBackNavigator.navigateBack(data.errorMessage) } @@ -221,11 +222,22 @@ fun AddEditPaymentScreen( }, text = { Text(text = employee.employeeName) + }, + leadingIcon = { + CircularBox( + icon = Icons.Default.Person, + doesSelected = false, + size = 30.dp, + showBorder = false, + text = employee.employeeName + ) } ) if(index != employees.size - 1) { - Divider(modifier = Modifier.fillMaxWidth(), color = Color.Gray, thickness = 0.8.dp) + Divider(modifier = Modifier + .fillMaxWidth() + .padding(start = 44.dp)) } } @@ -254,7 +266,7 @@ fun AddEditPaymentScreen( modifier = Modifier .fillMaxWidth(), onClick = { - navController.navigate(AddEditEmployeeScreenDestination()) + navController.navigate(Screens.AddEditEmployeeScreen) }, text = { Text( diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentState.kt b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentState.kt similarity index 52% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentState.kt rename to feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentState.kt index 9bb14b9f..ebc14c34 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentState.kt +++ b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentState.kt @@ -1,8 +1,8 @@ -package com.niyaj.poposroom.features.employee_payment.presentation.add_edit +package com.niyaj.employee_payment.add_edit -import com.niyaj.poposroom.features.common.utils.toMilliSecond -import com.niyaj.poposroom.features.employee.domain.utils.PaymentMode -import com.niyaj.poposroom.features.employee.domain.utils.PaymentType +import com.niyaj.common.utils.toMilliSecond +import com.niyaj.model.PaymentMode +import com.niyaj.model.PaymentType import java.time.LocalDate data class AddEditPaymentState( diff --git a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentViewModel.kt b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentViewModel.kt similarity index 92% rename from app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentViewModel.kt rename to feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentViewModel.kt index 37bec70e..27757f81 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/employee_payment/presentation/add_edit/AddEditPaymentViewModel.kt +++ b/feature/employee_payment/src/main/java/com/niyaj/employee_payment/add_edit/AddEditPaymentViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.employee_payment.presentation.add_edit +package com.niyaj.employee_payment.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,13 +7,13 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.employee.domain.model.Employee -import com.niyaj.poposroom.features.employee.domain.utils.PaymentMode -import com.niyaj.poposroom.features.employee_payment.domain.model.Payment -import com.niyaj.poposroom.features.employee_payment.domain.repository.PaymentRepository -import com.niyaj.poposroom.features.employee_payment.domain.repository.PaymentValidationRepository +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.PaymentRepository +import com.niyaj.data.repository.validation.PaymentValidationRepository +import com.niyaj.model.Employee +import com.niyaj.model.Payment +import com.niyaj.model.PaymentMode +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow diff --git a/feature/employee_payment/src/test/java/com/niyaj/employee_payment/ExampleUnitTest.kt b/feature/employee_payment/src/test/java/com/niyaj/employee_payment/ExampleUnitTest.kt new file mode 100644 index 00000000..a78839a1 --- /dev/null +++ b/feature/employee_payment/src/test/java/com/niyaj/employee_payment/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.employee_payment + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/expenses/.gitignore b/feature/expenses/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/expenses/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/expenses/build.gradle.kts b/feature/expenses/build.gradle.kts new file mode 100644 index 00000000..7f9afc9a --- /dev/null +++ b/feature/expenses/build.gradle.kts @@ -0,0 +1,32 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.expenses" + + ksp { + arg("compose-destinations.moduleName", "expenses") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + implementation(libs.dialog.core) + implementation(libs.dialog.datetime) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/expenses/proguard-rules.pro b/feature/expenses/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/expenses/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/expenses/src/androidTest/java/com/niyaj/expenses/ExampleInstrumentedTest.kt b/feature/expenses/src/androidTest/java/com/niyaj/expenses/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..527aaac3 --- /dev/null +++ b/feature/expenses/src/androidTest/java/com/niyaj/expenses/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.expenses + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.expenses", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/expenses/src/main/AndroidManifest.xml b/feature/expenses/src/main/AndroidManifest.xml new file mode 100644 index 00000000..74b7379f --- /dev/null +++ b/feature/expenses/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/ExpensesScreen.kt b/feature/expenses/src/main/java/com/niyaj/expenses/ExpensesScreen.kt similarity index 88% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/ExpensesScreen.kt rename to feature/expenses/src/main/java/com/niyaj/expenses/ExpensesScreen.kt index 1aae4030..7c3049b1 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/ExpensesScreen.kt +++ b/feature/expenses/src/main/java/com/niyaj/expenses/ExpensesScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.expenses.presentation +package com.niyaj.expenses import androidx.activity.compose.BackHandler import androidx.compose.foundation.BorderStroke @@ -19,7 +19,6 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Icon import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.CalendarMonth @@ -35,6 +34,7 @@ import androidx.compose.material3.CardDefaults import androidx.compose.material3.Divider import androidx.compose.material3.ElevatedCard import androidx.compose.material3.FabPosition +import androidx.compose.material3.Icon import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHostState @@ -53,34 +53,37 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.NoteText -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardOutlinedAssistChip -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.IconSizeSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.common.utils.toMilliSecond -import com.niyaj.poposroom.features.common.utils.toPrettyDate -import com.niyaj.poposroom.features.common.utils.toRupee -import com.niyaj.poposroom.features.destinations.AddEditExpenseScreenDestination -import com.niyaj.poposroom.features.expenses.domain.model.Expense -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.CREATE_NEW_EXPENSE -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.DELETE_EXPENSE_MESSAGE -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.DELETE_EXPENSE_TITLE -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_NOT_AVAIlABLE -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_SCREEN_TITLE -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_TAG -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.NO_ITEMS_IN_EXPENSE +import com.niyaj.common.utils.toMilliSecond +import com.niyaj.common.utils.toPrettyDate +import com.niyaj.common.utils.toRupee +import com.niyaj.data.utils.ExpenseTestTags.CREATE_NEW_EXPENSE +import com.niyaj.data.utils.ExpenseTestTags.DELETE_EXPENSE_MESSAGE +import com.niyaj.data.utils.ExpenseTestTags.DELETE_EXPENSE_TITLE +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_NOT_AVAIlABLE +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_SCREEN_TITLE +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_TAG +import com.niyaj.data.utils.ExpenseTestTags.NO_ITEMS_IN_EXPENSE +import com.niyaj.designsystem.theme.IconSizeSmall +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.expenses.destinations.AddEditExpenseScreenDestination +import com.niyaj.model.Expense +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.NoteText +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardOutlinedAssistChip +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.navigate import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.ResultRecipient @@ -90,7 +93,10 @@ import com.vanpra.composematerialdialogs.rememberMaterialDialogState import kotlinx.coroutines.launch import java.time.LocalDate -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.ExpensesScreen +) @Composable fun ExpensesScreen( navController: NavController, @@ -122,7 +128,6 @@ fun ExpensesScreen( LaunchedEffect(key1 = event) { event?.let { data -> when (data) { - is UiEvent.IsLoading -> {} is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) @@ -162,14 +167,7 @@ fun ExpensesScreen( StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) EXPENSE_SCREEN_TITLE else "${selectedItems.size} Selected", - placeholderText = EXPENSE_SEARCH_PLACEHOLDER, - showSearchBar = showSearchBar, - showSettings = false, - selectionCount = selectedItems.size, - searchText = searchText, - showBackButton = showSearchBar, floatingActionButton = { StandardFAB( showScrollToTop = lazyListState.isScrolled, @@ -185,19 +183,32 @@ fun ExpensesScreen( } ) }, - fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - navController.navigate(AddEditExpenseScreenDestination(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true + navActions = { + ScaffoldNavActions( + placeholderText = EXPENSE_SEARCH_PLACEHOLDER, + showSettingsIcon = true, + selectionCount = selectedItems.size, + showSearchIcon = showSearchBar, + searchText = searchText, + onEditClick = { + navController.navigate(AddEditExpenseScreenDestination(selectedItems.first())) + }, + onDeleteClick = { + openDialog.value = true + }, + onSettingsClick = {}, + onSelectAllClick = viewModel::selectAllItems, + onClearClick = viewModel::clearSearchText, + onSearchClick = viewModel::openSearchBar, + onSearchTextChanged = viewModel::searchTextChanged + ) }, + fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, - onSearchTextChanged = viewModel::searchTextChanged, - onSearchClick = viewModel::openSearchBar, onBackClick = viewModel::closeSearchBar, - onClearClick = viewModel::clearSearchText + snackbarHostState = snackbarState, ) { _ -> Column( modifier = Modifier diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/ExpensesViewModel.kt b/feature/expenses/src/main/java/com/niyaj/expenses/ExpensesViewModel.kt similarity index 81% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/ExpensesViewModel.kt rename to feature/expenses/src/main/java/com/niyaj/expenses/ExpensesViewModel.kt index ab5132fe..3bc0931d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/ExpensesViewModel.kt +++ b/feature/expenses/src/main/java/com/niyaj/expenses/ExpensesViewModel.kt @@ -1,15 +1,15 @@ -package com.niyaj.poposroom.features.expenses.presentation +package com.niyaj.expenses import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.startOfDayTime -import com.niyaj.poposroom.features.expenses.domain.repository.ExpenseRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.startOfDayTime +import com.niyaj.data.repository.ExpenseRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseEvent.kt b/feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseEvent.kt similarity index 86% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseEvent.kt rename to feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseEvent.kt index 2291b948..74b0f5f0 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseEvent.kt +++ b/feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseEvent.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.expenses.presentation.add_edit +package com.niyaj.expenses.add_edit sealed interface AddEditExpenseEvent { diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseScreen.kt b/feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseScreen.kt similarity index 82% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseScreen.kt rename to feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseScreen.kt index 74ff16ac..dbc585b2 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseScreen.kt +++ b/feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.expenses.presentation.add_edit +package com.niyaj.expenses.add_edit import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -11,8 +11,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.Divider -import androidx.compose.material.IconButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.ArrowForward @@ -23,11 +21,13 @@ import androidx.compose.material.icons.filled.Info import androidx.compose.material.icons.filled.NoteAdd import androidx.compose.material.icons.filled.PhoneAndroid import androidx.compose.material.icons.filled.Radar +import androidx.compose.material3.Divider import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.ListItem import androidx.compose.material3.ListItemDefaults import androidx.compose.material3.MaterialTheme @@ -53,25 +53,25 @@ import androidx.compose.ui.window.PopupProperties import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardOutlinedTextField -import com.niyaj.poposroom.features.common.components.StandardScaffoldWithOutDrawer -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.toMilliSecond -import com.niyaj.poposroom.features.common.utils.toPrettyDate -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.ADD_EDIT_EXPENSE_BUTTON -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.CREATE_NEW_EXPENSE -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EDIT_EXPENSE_ITEM -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_AMOUNT_ERROR -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_AMOUNT_FIELD -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_DATE_ERROR -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_DATE_FIELD -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_NAME_ERROR -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_NAME_FIELD -import com.niyaj.poposroom.features.expenses.domain.utils.ExpenseTestTags.EXPENSE_NOTE_FIELD +import com.niyaj.common.utils.toMilliSecond +import com.niyaj.common.utils.toPrettyDate +import com.niyaj.data.utils.ExpenseTestTags.ADD_EDIT_EXPENSE_BUTTON +import com.niyaj.data.utils.ExpenseTestTags.CREATE_NEW_EXPENSE +import com.niyaj.data.utils.ExpenseTestTags.EDIT_EXPENSE_ITEM +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_AMOUNT_ERROR +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_AMOUNT_FIELD +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_DATE_ERROR +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_DATE_FIELD +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_NAME_ERROR +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_NAME_FIELD +import com.niyaj.data.utils.ExpenseTestTags.EXPENSE_NOTE_FIELD +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.utils.UiEvent import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.result.ResultBackNavigator import com.vanpra.composematerialdialogs.MaterialDialog @@ -86,7 +86,7 @@ fun AddEditExpenseScreen( expenseId: Int = 0, navController: NavController, viewModel: AddEditExpenseViewModel = hiltViewModel(), - resultBackNavigator: ResultBackNavigator + resultBackNavigator: ResultBackNavigator, ) { val dateError = viewModel.dateError.collectAsStateWithLifecycle().value @@ -102,11 +102,11 @@ fun AddEditExpenseScreen( LaunchedEffect(key1 = event) { event?.let { data -> - when(data) { - is UiEvent.IsLoading -> {} + when (data) { is UiEvent.OnError -> { resultBackNavigator.navigateBack(data.errorMessage) } + is UiEvent.OnSuccess -> { resultBackNavigator.navigateBack(data.successMessage) } @@ -149,7 +149,7 @@ fun AddEditExpenseScreen( modifier = Modifier .fillMaxWidth() .padding(SpaceSmall), - verticalArrangement = Arrangement.Center, + verticalArrangement = Arrangement.spacedBy(SpaceSmall), ) { item(EXPENSE_NAME_FIELD) { Column( @@ -180,7 +180,7 @@ fun AddEditExpenseScreen( }, ) - if(expensesNames.isNotEmpty()) { + if (expensesNames.isNotEmpty()) { DropdownMenu( modifier = Modifier .heightIn(max = 200.dp) @@ -210,15 +210,17 @@ fun AddEditExpenseScreen( contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding, ) - if(index != expensesNames.size - 1) { - Divider(modifier = Modifier.fillMaxWidth(), color = Color.Gray, thickness = 0.8.dp) + if (index != expensesNames.size - 1) { + Divider( + modifier = Modifier.fillMaxWidth(), + color = Color.Gray, + thickness = 0.8.dp + ) } } } } } - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(EXPENSE_AMOUNT_FIELD) { @@ -234,8 +236,6 @@ fun AddEditExpenseScreen( viewModel.onEvent(AddEditExpenseEvent.ExpensesAmountChanged(it)) } ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(EXPENSE_DATE_FIELD) { @@ -247,7 +247,10 @@ fun AddEditExpenseScreen( IconButton( onClick = { dialogState.show() } ) { - Icon(imageVector = Icons.Default.CalendarMonth, contentDescription = "Choose a date") + Icon( + imageVector = Icons.Default.CalendarMonth, + contentDescription = "Choose a date" + ) } }, readOnly = true, @@ -265,8 +268,6 @@ fun AddEditExpenseScreen( } } ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(EXPENSE_NOTE_FIELD) { @@ -278,8 +279,6 @@ fun AddEditExpenseScreen( viewModel.onEvent(AddEditExpenseEvent.ExpensesNoteChanged(it)) } ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item("ExistError") { @@ -305,7 +304,6 @@ fun AddEditExpenseScreen( ) ) - Spacer(modifier = Modifier.height(SpaceSmall)) } } } @@ -322,7 +320,7 @@ fun AddEditExpenseScreen( allowedDateValidator = { date -> date <= LocalDate.now() } - ) {date -> + ) { date -> viewModel.onEvent(AddEditExpenseEvent.ExpensesDateChanged(date.toMilliSecond)) } } diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseState.kt b/feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseState.kt similarity index 69% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseState.kt rename to feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseState.kt index 73c3b8ec..617fa681 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseState.kt +++ b/feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseState.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.expenses.presentation.add_edit +package com.niyaj.expenses.add_edit -import com.niyaj.poposroom.features.common.utils.toMilliSecond +import com.niyaj.common.utils.toMilliSecond import java.time.LocalDate data class AddEditExpenseState( diff --git a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseViewModel.kt b/feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseViewModel.kt similarity index 92% rename from app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseViewModel.kt rename to feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseViewModel.kt index 847f3496..8b6b71b0 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/expenses/presentation/add_edit/AddEditExpenseViewModel.kt +++ b/feature/expenses/src/main/java/com/niyaj/expenses/add_edit/AddEditExpenseViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.expenses.presentation.add_edit +package com.niyaj.expenses.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,12 +7,12 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.capitalizeWords -import com.niyaj.poposroom.features.expenses.domain.model.Expense -import com.niyaj.poposroom.features.expenses.domain.repository.ExpenseRepository -import com.niyaj.poposroom.features.expenses.domain.repository.ExpenseValidationRepository +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.capitalizeWords +import com.niyaj.data.repository.ExpenseRepository +import com.niyaj.data.repository.ExpenseValidationRepository +import com.niyaj.model.Expense +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow diff --git a/feature/expenses/src/test/java/com/niyaj/expenses/ExampleUnitTest.kt b/feature/expenses/src/test/java/com/niyaj/expenses/ExampleUnitTest.kt new file mode 100644 index 00000000..b75bfc53 --- /dev/null +++ b/feature/expenses/src/test/java/com/niyaj/expenses/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.expenses + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/home/.gitignore b/feature/home/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/home/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts new file mode 100644 index 00000000..ce5a250d --- /dev/null +++ b/feature/home/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.home" + + ksp { + arg("compose-destinations.moduleName", "home") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/home/proguard-rules.pro b/feature/home/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/home/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/home/src/androidTest/java/com/niyaj/home/ExampleInstrumentedTest.kt b/feature/home/src/androidTest/java/com/niyaj/home/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..4accfc72 --- /dev/null +++ b/feature/home/src/androidTest/java/com/niyaj/home/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.home + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.home", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/home/src/main/AndroidManifest.xml b/feature/home/src/main/AndroidManifest.xml new file mode 100644 index 00000000..44008a43 --- /dev/null +++ b/feature/home/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/main_feed/presentation/MainFeedScreen.kt b/feature/home/src/main/java/com/niyaj/home/HomeScreen.kt similarity index 86% rename from app/src/main/java/com/niyaj/poposroom/features/main_feed/presentation/MainFeedScreen.kt rename to feature/home/src/main/java/com/niyaj/home/HomeScreen.kt index f2e832de..e0d2c9a7 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/main_feed/presentation/MainFeedScreen.kt +++ b/feature/home/src/main/java/com/niyaj/home/HomeScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.main_feed.presentation +package com.niyaj.home import androidx.activity.compose.BackHandler import androidx.compose.animation.Crossfade @@ -49,37 +49,37 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.R -import com.niyaj.poposroom.features.common.components.CircularBoxWithQty -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardScaffoldWithBottomNavigation -import com.niyaj.poposroom.features.common.components.TitleWithIcon -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.common.utils.isScrollingUp -import com.niyaj.poposroom.features.common.utils.toRupee -import com.niyaj.poposroom.features.destinations.AddEditCartOrderScreenDestination -import com.niyaj.poposroom.features.destinations.AddEditProductScreenDestination -import com.niyaj.poposroom.features.main_feed.domain.model.ProductWithFlowQuantity -import com.niyaj.poposroom.features.main_feed.domain.utils.MainFeedTestTags -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags -import com.niyaj.poposroom.features.product.presentation.CategoriesData +import com.niyaj.common.utils.toRupee +import com.niyaj.core.ui.R +import com.niyaj.data.utils.MainFeedTestTags +import com.niyaj.data.utils.ProductTestTags +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.ProductWithFlowQuantity +import com.niyaj.ui.components.CategoriesData +import com.niyaj.ui.components.CircularBoxWithQty +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.StandardScaffoldWithBottomNavigation +import com.niyaj.ui.components.TitleWithIcon +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled +import com.niyaj.ui.utils.isScrollingUp import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.RootNavGraph -import com.ramcosta.composedestinations.navigation.navigate import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch -@Destination @RootNavGraph(start = true) +@Destination( + route = Screens.HomeScreen +) @Composable -fun MainFeedScreen( +fun HomeScreen( navController: NavController, - viewModel: MainFeedViewModel = hiltViewModel() + viewModel: MainFeedViewModel = hiltViewModel(), ) { val scope = rememberCoroutineScope() val lazyListState = rememberLazyListState() @@ -100,12 +100,12 @@ fun MainFeedScreen( LaunchedEffect(key1 = true) { viewModel.eventFlow.collectLatest { event -> when (event) { - is UiEvent.IsLoading -> {} is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(event.errorMessage) } } + is UiEvent.OnSuccess -> { scope.launch { snackbarState.showSnackbar(event.successMessage) @@ -153,7 +153,7 @@ fun MainFeedScreen( image = painterResource(id = R.drawable.nothinghere), buttonText = MainFeedTestTags.CREATE_NEW_PRODUCT, onClick = { - navController.navigate(AddEditProductScreenDestination()) + navController.navigate(Screens.AddEditProductScreen) } ) } @@ -177,14 +177,14 @@ fun MainFeedScreen( if (selectedId != 0) { viewModel.addProductToCart(selectedId, it) } else { - navController.navigate(AddEditCartOrderScreenDestination()) + navController.navigate(Screens.AddEditCartOrderScreen) } }, onDecrease = { viewModel.removeProductFromCart(selectedId, it) }, onCreateProduct = { - navController.navigate(AddEditProductScreenDestination()) + navController.navigate(Screens.AddEditProductScreen) } ) } @@ -344,7 +344,12 @@ fun IncDecBox( .fillMaxHeight(), onClick = onDecrease, enabled = enableDecreasing, - shape = RoundedCornerShape(topStart = SpaceMini, topEnd = 0.dp, bottomStart = SpaceMini, bottomEnd = 0.dp), + shape = RoundedCornerShape( + topStart = SpaceMini, + topEnd = 0.dp, + bottomStart = SpaceMini, + bottomEnd = 0.dp + ), colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.onPrimary ) diff --git a/app/src/main/java/com/niyaj/poposroom/features/main_feed/presentation/MainFeedViewModel.kt b/feature/home/src/main/java/com/niyaj/home/MainFeedViewModel.kt similarity index 88% rename from app/src/main/java/com/niyaj/poposroom/features/main_feed/presentation/MainFeedViewModel.kt rename to feature/home/src/main/java/com/niyaj/home/MainFeedViewModel.kt index fb9a3bb2..1b537e8f 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/main_feed/presentation/MainFeedViewModel.kt +++ b/feature/home/src/main/java/com/niyaj/home/MainFeedViewModel.kt @@ -1,13 +1,13 @@ -package com.niyaj.poposroom.features.main_feed.presentation +package com.niyaj.home import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.cart.domain.repository.CartRepository -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.main_feed.domain.repository.MainFeedRepository +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.CartRepository +import com.niyaj.data.repository.MainFeedRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow diff --git a/feature/home/src/test/java/com/niyaj/home/ExampleUnitTest.kt b/feature/home/src/test/java/com/niyaj/home/ExampleUnitTest.kt new file mode 100644 index 00000000..8a5adb99 --- /dev/null +++ b/feature/home/src/test/java/com/niyaj/home/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.home + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/order/.gitignore b/feature/order/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/order/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/order/build.gradle.kts b/feature/order/build.gradle.kts new file mode 100644 index 00000000..e20f743e --- /dev/null +++ b/feature/order/build.gradle.kts @@ -0,0 +1,35 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.order" + + ksp { + arg("compose-destinations.moduleName", "order") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.permissions) + implementation(libs.revealswipe) + implementation(libs.dialog.core) + implementation(libs.dialog.datetime) + implementation(libs.pos.printer) + + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/order/proguard-rules.pro b/feature/order/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/order/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/order/src/androidTest/java/com/niyaj/order/ExampleInstrumentedTest.kt b/feature/order/src/androidTest/java/com/niyaj/order/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..a9826b0e --- /dev/null +++ b/feature/order/src/androidTest/java/com/niyaj/order/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.order + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.order", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/order/src/main/AndroidManifest.xml b/feature/order/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/order/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderEvent.kt b/feature/order/src/main/java/com/niyaj/order/OrderEvent.kt similarity index 82% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderEvent.kt rename to feature/order/src/main/java/com/niyaj/order/OrderEvent.kt index 19004be7..a80ae205 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderEvent.kt +++ b/feature/order/src/main/java/com/niyaj/order/OrderEvent.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.order.presentation +package com.niyaj.order sealed class OrderEvent { diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderScreen.kt b/feature/order/src/main/java/com/niyaj/order/OrderScreen.kt similarity index 78% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderScreen.kt rename to feature/order/src/main/java/com/niyaj/order/OrderScreen.kt index bb2ecabc..68e3a1be 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderScreen.kt +++ b/feature/order/src/main/java/com/niyaj/order/OrderScreen.kt @@ -1,7 +1,6 @@ -package com.niyaj.poposroom.features.order.presentation +package com.niyaj.order import android.Manifest -import android.app.Activity import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothManager import android.content.Intent @@ -14,14 +13,17 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.material.Icon -import androidx.compose.material.IconButton import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.CalendarMonth import androidx.compose.material.icons.filled.DeliveryDining import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Today +import androidx.compose.material.icons.outlined.CalendarMonth +import androidx.compose.material.icons.outlined.DeliveryDining +import androidx.compose.material.icons.outlined.Today +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarDuration import androidx.compose.material3.SnackbarHostState @@ -41,22 +43,24 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberMultiplePermissionsState -import com.niyaj.poposroom.features.cart.presentation.components.CartTabItem -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.common.components.NAV_SEARCH_BTN -import com.niyaj.poposroom.features.common.components.StandardOutlinedAssistChip -import com.niyaj.poposroom.features.common.components.StandardScaffoldNew -import com.niyaj.poposroom.features.common.components.StandardSearchBar -import com.niyaj.poposroom.features.common.components.Tabs -import com.niyaj.poposroom.features.common.components.TabsContent -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.toMilliSecond -import com.niyaj.poposroom.features.common.utils.toPrettyDate -import com.niyaj.poposroom.features.destinations.CartScreenDestination -import com.niyaj.poposroom.features.order.domain.utils.OrderTestTags.DELETE_ORDER -import com.niyaj.poposroom.features.order.domain.utils.OrderTestTags.DELETE_ORDER_MESSAGE -import com.niyaj.poposroom.features.order.presentation.components.OrderedItemLayout +import com.niyaj.common.utils.toMilliSecond +import com.niyaj.common.utils.toPrettyDate +import com.niyaj.data.utils.OrderTestTags.DELETE_ORDER +import com.niyaj.data.utils.OrderTestTags.DELETE_ORDER_MESSAGE +import com.niyaj.model.OrderType +import com.niyaj.order.components.OrderTab +import com.niyaj.order.components.OrderTabs +import com.niyaj.order.components.OrderTabsContent +import com.niyaj.order.components.OrderedItemLayout +import com.niyaj.order.destinations.OrderDetailsScreenDestination +import com.niyaj.ui.components.NAV_SEARCH_BTN +import com.niyaj.ui.components.StandardOutlinedAssistChip +import com.niyaj.ui.components.StandardScaffoldNew +import com.niyaj.ui.components.StandardSearchBar +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.navigate import com.vanpra.composematerialdialogs.MaterialDialog import com.vanpra.composematerialdialogs.datetime.date.datepicker @@ -64,14 +68,17 @@ import com.vanpra.composematerialdialogs.message import com.vanpra.composematerialdialogs.rememberMaterialDialogState import com.vanpra.composematerialdialogs.title import kotlinx.coroutines.flow.collectLatest -import timber.log.Timber import java.time.LocalDate @OptIn(ExperimentalFoundationApi::class, ExperimentalPermissionsApi::class) -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.OrderScreen +) @Composable fun OrderScreen( navController: NavController, + onClickEditOrder: (Int) -> Unit, viewModel: OrderViewModel = hiltViewModel(), ) { val pagerState = rememberPagerState( @@ -105,13 +112,7 @@ fun OrderScreen( val enableBluetoothContract = rememberLauncherForActivityResult( ActivityResultContracts.StartActivityForResult() - ) { - if (it.resultCode == Activity.RESULT_OK) { - Timber.d("bluetoothLauncher", "Success") - } else { - Timber.w("bluetoothLauncher", "Failed") - } - } + ) {} // This intent will open the enable bluetooth dialog val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) @@ -142,6 +143,7 @@ fun OrderScreen( val printOrder: (Int) -> Unit = { if (bluetoothPermissions.allPermissionsGranted) { if (bluetoothAdapter?.isEnabled == true) { +// Todo: link print feature to print order // Bluetooth is on print the receipt // printViewModel.onPrintEvent(PrintEvent.PrintOrder(it)) } else { @@ -182,17 +184,17 @@ fun OrderScreen( var deletableOrder by remember { mutableIntStateOf(0) } - LaunchedEffect(key1 = true){ + LaunchedEffect(key1 = true) { viewModel.eventFlow.collectLatest { event -> - when(event){ + when (event) { is UiEvent.OnSuccess -> { val result = snackbarHostState.showSnackbar( message = event.successMessage, actionLabel = "View", duration = SnackbarDuration.Short ) - if(result == SnackbarResult.ActionPerformed){ - navController.navigate(CartScreenDestination()) + if (result == SnackbarResult.ActionPerformed) { + navController.navigate(Screens.CartScreen) } } @@ -202,16 +204,14 @@ fun OrderScreen( duration = SnackbarDuration.Short ) } - - is UiEvent.IsLoading -> {} } } } BackHandler(true) { - if (showSearchBar){ + if (showSearchBar) { viewModel.closeSearchBar() - } else{ + } else { navController.navigateUp() } } @@ -224,36 +224,39 @@ fun OrderScreen( onBackClick = viewModel::closeSearchBar, title = "Orders", navActions = { - if(showSearchBar){ + if (showSearchBar) { StandardSearchBar( searchText = viewModel.searchText.value, placeholderText = "Search for orders...", onSearchTextChanged = viewModel::searchTextChanged, onClearClick = viewModel::clearSearchText, ) - } - else { - val showIcon = if(pagerState.currentPage == 1) dineInOrders.isNotEmpty() else dineOutOrders.isNotEmpty() + } else { + val showIcon = + if (pagerState.currentPage == 1) dineInOrders.isNotEmpty() else dineOutOrders.isNotEmpty() if (selectedDate.isNotEmpty()) { StandardOutlinedAssistChip( text = selectedDate.toPrettyDate(), - icon = Icons.Default.CalendarMonth, + icon = Icons.Outlined.CalendarMonth, onClick = { dialogState.show() }, trailingIcon = Icons.Default.ArrowDropDown, borderColor = MaterialTheme.colorScheme.outline, ) - }else{ + } else { IconButton( onClick = { dialogState.show() } ) { - Icon(imageVector = Icons.Default.Today, contentDescription = "Choose Date") + Icon( + imageVector = Icons.Outlined.Today, + contentDescription = "Choose Date" + ) } } - if (showIcon){ + if (showIcon) { IconButton( onClick = viewModel::openSearchBar ) { @@ -263,14 +266,14 @@ fun OrderScreen( ) } - if(pagerState.currentPage == 0){ + if (pagerState.currentPage == 0) { IconButton( onClick = { printDeliveryReport() } ) { Icon( - imageVector = Icons.Default.DeliveryDining, + imageVector = Icons.Outlined.DeliveryDining, contentDescription = "Print Delivery Reports", ) } @@ -278,7 +281,7 @@ fun OrderScreen( } } } - ){ + ) { MaterialDialog( dialogState = deleteOrderState, buttons = { @@ -319,9 +322,8 @@ fun OrderScreen( } val tabs = listOf( - CartTabItem.DineOutItem { + OrderTab.DineOutOrder { OrderedItemLayout( - navController = navController, orders = dineOutOrders, isLoading = isLoading, showSearchBar = showSearchBar, @@ -334,12 +336,18 @@ fun OrderScreen( }, onMarkedAsProcessing = { viewModel.onOrderEvent(OrderEvent.MarkedAsProcessing(it)) - } + }, + onNavigateToHomeScreen = { + navController.navigate(Screens.HomeScreen) + }, + onClickOrderDetails = { + navController.navigate(OrderDetailsScreenDestination(it)) + }, + onClickEditOrder = onClickEditOrder ) }, - CartTabItem.DineInItem { + OrderTab.DineInOrder { OrderedItemLayout( - navController = navController, orders = dineInOrders, isLoading = isLoading, showSearchBar = showSearchBar, @@ -352,7 +360,14 @@ fun OrderScreen( }, onMarkedAsProcessing = { viewModel.onOrderEvent(OrderEvent.MarkedAsProcessing(it)) - } + }, + onNavigateToHomeScreen = { + navController.navigate(Screens.HomeScreen) + }, + onClickOrderDetails = { + navController.navigate(OrderDetailsScreenDestination(it)) + }, + onClickEditOrder = onClickEditOrder ) } ) @@ -363,8 +378,8 @@ fun OrderScreen( horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.Top, ) { - Tabs(tabs = tabs, pagerState = pagerState) - TabsContent(tabs = tabs, pagerState = pagerState) + OrderTabs(tabs = tabs, pagerState = pagerState) + OrderTabsContent(tabs = tabs, pagerState = pagerState) } } } \ No newline at end of file diff --git a/feature/order/src/main/java/com/niyaj/order/OrderState.kt b/feature/order/src/main/java/com/niyaj/order/OrderState.kt new file mode 100644 index 00000000..d0dd6cde --- /dev/null +++ b/feature/order/src/main/java/com/niyaj/order/OrderState.kt @@ -0,0 +1,8 @@ +package com.niyaj.order + +import com.niyaj.model.Order + +data class OrderState( + val orders: List = emptyList(), + val isLoading: Boolean = true, +) diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderViewModel.kt b/feature/order/src/main/java/com/niyaj/order/OrderViewModel.kt similarity index 89% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderViewModel.kt rename to feature/order/src/main/java/com/niyaj/order/OrderViewModel.kt index f9ec521d..45a4f7ce 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/OrderViewModel.kt +++ b/feature/order/src/main/java/com/niyaj/order/OrderViewModel.kt @@ -1,12 +1,12 @@ -package com.niyaj.poposroom.features.order.presentation +package com.niyaj.order import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope import com.dantsu.escposprinter.EscPosPrinter -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.order.domain.repository.OrderRepository +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.OrderRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow diff --git a/feature/order/src/main/java/com/niyaj/order/components/OrderTab.kt b/feature/order/src/main/java/com/niyaj/order/components/OrderTab.kt new file mode 100644 index 00000000..46da0230 --- /dev/null +++ b/feature/order/src/main/java/com/niyaj/order/components/OrderTab.kt @@ -0,0 +1,36 @@ +package com.niyaj.order.components + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DeliveryDining +import androidx.compose.material.icons.filled.Dining +import androidx.compose.runtime.Composable +import androidx.compose.ui.graphics.vector.ImageVector + +typealias ComposableFunction = @Composable () -> Unit + +sealed class OrderTab( + val icon: ImageVector, + val title: String, + val screen: ComposableFunction, +) { + + data class DineInOrder( + val content: @Composable () -> Unit = {}, + ) : OrderTab( + icon = Icons.Default.Dining, + title = "DineIn", + screen = { + content() + } + ) + + data class DineOutOrder( + val content: @Composable () -> Unit = {}, + ) : OrderTab( + icon = Icons.Default.DeliveryDining, + title = "DineOut", + screen = { + content() + } + ) +} diff --git a/feature/order/src/main/java/com/niyaj/order/components/OrderTabs.kt b/feature/order/src/main/java/com/niyaj/order/components/OrderTabs.kt new file mode 100644 index 00000000..f1d18e45 --- /dev/null +++ b/feature/order/src/main/java/com/niyaj/order/components/OrderTabs.kt @@ -0,0 +1,69 @@ +package com.niyaj.order.components + +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.PagerState +import androidx.compose.foundation.shape.CutCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.LeadingIconTab +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.TabRow +import androidx.compose.material3.TabRowDefaults +import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.niyaj.designsystem.theme.SpaceMini +import kotlinx.coroutines.launch + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun OrderTabs(tabs: List, pagerState: PagerState) { + val scope = rememberCoroutineScope() + + // OR ScrollableTabRow() + TabRow( + // Our selected tab is our current page + selectedTabIndex = pagerState.currentPage, + // Override the indicator, using the provided pagerTabIndicatorOffset modifier + indicator = { tabPositions -> + if (pagerState.currentPage < tabPositions.size) { + TabRowDefaults.PrimaryIndicator( + Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]), + width = 80.dp, + height = 4.dp, + shape = CutCornerShape(topStart = SpaceMini, topEnd = SpaceMini) + ) + } + } + ) { + // Add tabs for all of our pages + tabs.forEachIndexed { index, tab -> + LeadingIconTab( + icon = { Icon(imageVector = tab.icon, contentDescription = "") }, + text = { Text(tab.title) }, + selected = pagerState.currentPage == index, + onClick = { + scope.launch { + pagerState.animateScrollToPage(index) + } + }, + unselectedContentColor = MaterialTheme.colorScheme.outline, + modifier = Modifier.fillMaxWidth() + ) + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun OrderTabsContent(tabs: List, pagerState: PagerState) { + HorizontalPager( + state = pagerState, + ) { page -> + tabs[page].screen() + } +} diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/OrderedItem.kt b/feature/order/src/main/java/com/niyaj/order/components/OrderedItem.kt similarity index 51% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/OrderedItem.kt rename to feature/order/src/main/java/com/niyaj/order/components/OrderedItem.kt index 71bca981..031a6a9e 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/OrderedItem.kt +++ b/feature/order/src/main/java/com/niyaj/order/components/OrderedItem.kt @@ -1,24 +1,26 @@ -package com.niyaj.poposroom.features.order.presentation.components +package com.niyaj.order.components -import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.Icon -import androidx.compose.material.IconButton import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.Restore +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.LightColor9 -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.order.domain.model.Order +import com.niyaj.designsystem.theme.LightColor9 +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.Order import de.charlex.compose.RevealSwipe @OptIn(ExperimentalMaterialApi::class) @@ -34,44 +36,59 @@ fun OrderedItem( RevealSwipe( modifier = Modifier .fillMaxWidth(), - onContentClick = {}, + onContentClick = { + onClickViewDetails(order.orderId) + }, maxRevealDp = 150.dp, hiddenContentStart = { - IconButton( - onClick = { - onMarkedAsProcessing(order.orderId) - } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceSmall), + horizontalArrangement = Arrangement.spacedBy(SpaceSmall, Alignment.Start), + verticalAlignment = Alignment.CenterVertically, ) { - Icon( - imageVector = Icons.Default.Add, - contentDescription = null, - modifier = Modifier.padding(horizontal = 25.dp), - ) - } - - Spacer(modifier = Modifier.width(SpaceSmall)) + IconButton( + onClick = { + onMarkedAsProcessing(order.orderId) + } + ) { + Icon( + imageVector = Icons.Default.Restore, + contentDescription = "Mark as processing", + tint = MaterialTheme.colorScheme.onPrimary, + modifier = Modifier.size(24.dp) + ) + } - IconButton( - onClick = { - onClickEdit(order.orderId) + IconButton( + onClick = { + onClickEdit(order.orderId) + } + ) { + Icon( + imageVector = Icons.Default.Edit, + contentDescription = "Edit Order", + tint = MaterialTheme.colorScheme.onPrimary, + ) } - ) { - Icon(imageVector = Icons.Default.Edit, contentDescription = null) } }, hiddenContentEnd = { IconButton( onClick = { onClickDelete(order.orderId) - } + }, + modifier = Modifier.padding(horizontal = 25.dp), ) { Icon( imageVector = Icons.Default.Delete, contentDescription = "Delete order", - modifier = Modifier.padding(horizontal = 25.dp), + tint = MaterialTheme.colorScheme.onSecondary, ) } }, + animateBackgroundCardColor = true, contentColor = MaterialTheme.colorScheme.onSurface, backgroundCardContentColor = LightColor9, backgroundCardStartColor = MaterialTheme.colorScheme.primary, diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/OrderedItemData.kt b/feature/order/src/main/java/com/niyaj/order/components/OrderedItemData.kt similarity index 92% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/OrderedItemData.kt rename to feature/order/src/main/java/com/niyaj/order/components/OrderedItemData.kt index 6b18c824..e8b7fdbf 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/OrderedItemData.kt +++ b/feature/order/src/main/java/com/niyaj/order/components/OrderedItemData.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.order.presentation.components +package com.niyaj.order.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -29,13 +29,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.R -import com.niyaj.poposroom.features.common.components.TextWithIcon -import com.niyaj.poposroom.features.common.ui.theme.LightColor8 -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.toTime -import com.niyaj.poposroom.features.order.domain.model.Order +import com.niyaj.common.utils.toTime +import com.niyaj.core.ui.R +import com.niyaj.designsystem.theme.LightColor8 +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.Order +import com.niyaj.ui.components.TextWithIcon @Composable fun OrderedItemData( diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/OrderedItemLayout.kt b/feature/order/src/main/java/com/niyaj/order/components/OrderedItemLayout.kt similarity index 50% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/OrderedItemLayout.kt rename to feature/order/src/main/java/com/niyaj/order/components/OrderedItemLayout.kt index ad019eaf..7d3a9f70 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/OrderedItemLayout.kt +++ b/feature/order/src/main/java/com/niyaj/order/components/OrderedItemLayout.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.order.presentation.components +package com.niyaj.order.components import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -10,40 +10,37 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource -import androidx.navigation.NavController -import com.niyaj.poposroom.R -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.destinations.AddEditCartOrderScreenDestination -import com.niyaj.poposroom.features.destinations.MainFeedScreenDestination -import com.niyaj.poposroom.features.destinations.OrderDetailsScreenDestination -import com.niyaj.poposroom.features.order.domain.model.Order -import com.niyaj.poposroom.features.order.domain.utils.OrderTestTags.ADD_ITEM_TO_CART -import com.niyaj.poposroom.features.order.domain.utils.OrderTestTags.ORDER_NOT_AVAILABLE -import com.niyaj.poposroom.features.order.domain.utils.OrderTestTags.SEARCH_ORDER_NOT_AVAILABLE -import com.ramcosta.composedestinations.navigation.navigate +import com.niyaj.core.ui.R +import com.niyaj.data.utils.OrderTestTags.ADD_ITEM_TO_CART +import com.niyaj.data.utils.OrderTestTags.ORDER_NOT_AVAILABLE +import com.niyaj.data.utils.OrderTestTags.SEARCH_ORDER_NOT_AVAILABLE +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.Order +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator @Composable fun OrderedItemLayout( - navController : NavController, orders: List, isLoading: Boolean = false, showSearchBar: Boolean = false, onClickPrintOrder: (Int) -> Unit, onClickDelete: (Int) -> Unit, onMarkedAsProcessing: (Int) -> Unit, + onNavigateToHomeScreen: () -> Unit, + onClickOrderDetails: (Int) -> Unit, + onClickEditOrder: (Int) -> Unit, ) { - if(orders.isEmpty()){ + if (orders.isEmpty()) { ItemNotAvailable( - text = if(showSearchBar) SEARCH_ORDER_NOT_AVAILABLE else ORDER_NOT_AVAILABLE, + text = if (showSearchBar) SEARCH_ORDER_NOT_AVAILABLE else ORDER_NOT_AVAILABLE, buttonText = ADD_ITEM_TO_CART, image = painterResource(R.drawable.emptycarttwo), onClick = { - navController.navigate(MainFeedScreenDestination()) + onNavigateToHomeScreen() } ) - } else if(isLoading){ + } else if (isLoading) { LoadingIndicator() } else { LazyColumn( @@ -51,24 +48,20 @@ fun OrderedItemLayout( .fillMaxSize() .padding(SpaceSmall), horizontalAlignment = Alignment.Start, - ){ + ) { items( items = orders, key = { it.orderId } - ){ order -> + ) { order -> OrderedItem( order = order, onClickPrintOrder = onClickPrintOrder, onMarkedAsProcessing = onMarkedAsProcessing, onClickDelete = onClickDelete, - onClickViewDetails = { - navController.navigate(OrderDetailsScreenDestination(it)) - }, - onClickEdit = { - navController.navigate(AddEditCartOrderScreenDestination(it)) - }, + onClickViewDetails = onClickOrderDetails, + onClickEdit = onClickEditOrder, ) Spacer(modifier = Modifier.height(SpaceSmall)) } diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/TextDivider.kt b/feature/order/src/main/java/com/niyaj/order/components/TextDivider.kt similarity index 94% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/TextDivider.kt rename to feature/order/src/main/java/com/niyaj/order/components/TextDivider.kt index ddf02330..abe00f4c 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/TextDivider.kt +++ b/feature/order/src/main/java/com/niyaj/order/components/TextDivider.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.order.presentation.components +package com.niyaj.order.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/ThreeGridTexts.kt b/feature/order/src/main/java/com/niyaj/order/components/ThreeGridTexts.kt similarity index 95% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/ThreeGridTexts.kt rename to feature/order/src/main/java/com/niyaj/order/components/ThreeGridTexts.kt index 4bcf7a91..02f4e107 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/ThreeGridTexts.kt +++ b/feature/order/src/main/java/com/niyaj/order/components/ThreeGridTexts.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.order.presentation.components +package com.niyaj.order.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/TwoGridTexts.kt b/feature/order/src/main/java/com/niyaj/order/components/TwoGridTexts.kt similarity index 94% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/TwoGridTexts.kt rename to feature/order/src/main/java/com/niyaj/order/components/TwoGridTexts.kt index 9106a373..2516115d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/components/TwoGridTexts.kt +++ b/feature/order/src/main/java/com/niyaj/order/components/TwoGridTexts.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.order.presentation.components +package com.niyaj.order.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/details/OrderDetailsScreen.kt b/feature/order/src/main/java/com/niyaj/order/details/OrderDetailsScreen.kt similarity index 93% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/details/OrderDetailsScreen.kt rename to feature/order/src/main/java/com/niyaj/order/details/OrderDetailsScreen.kt index 7b83617d..e9eb72a6 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/details/OrderDetailsScreen.kt +++ b/feature/order/src/main/java/com/niyaj/order/details/OrderDetailsScreen.kt @@ -1,7 +1,6 @@ -package com.niyaj.poposroom.features.order.presentation.details +package com.niyaj.order.details import android.Manifest -import android.app.Activity import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothManager import android.content.Intent @@ -63,42 +62,45 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.rememberMultiplePermissionsState -import com.niyaj.poposroom.features.addon_item.domain.model.AddOnItem -import com.niyaj.poposroom.features.address.domain.model.Address -import com.niyaj.poposroom.features.cart.domain.model.CartProductItem -import com.niyaj.poposroom.features.cart.domain.model.OrderPrice -import com.niyaj.poposroom.features.cart_order.domain.model.CartOrder -import com.niyaj.poposroom.features.cart_order.domain.utils.OrderType -import com.niyaj.poposroom.features.charges.domain.model.Charges -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardExpandable -import com.niyaj.poposroom.features.common.components.StandardOutlinedChip -import com.niyaj.poposroom.features.common.components.StandardScaffoldNew -import com.niyaj.poposroom.features.common.components.TextWithIcon -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.LightColor8 -import com.niyaj.poposroom.features.common.ui.theme.Pewter -import com.niyaj.poposroom.features.common.ui.theme.SpaceMedium -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.toPrettyDate -import com.niyaj.poposroom.features.common.utils.toRupee -import com.niyaj.poposroom.features.customer.domain.model.Customer -import com.niyaj.poposroom.features.order.presentation.components.TextDivider -import com.niyaj.poposroom.features.order.presentation.components.ThreeGridTexts -import com.niyaj.poposroom.features.order.presentation.components.TwoGridTexts +import com.niyaj.common.utils.toPrettyDate +import com.niyaj.common.utils.toRupee +import com.niyaj.designsystem.theme.LightColor8 +import com.niyaj.designsystem.theme.Pewter +import com.niyaj.designsystem.theme.SpaceMedium +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.AddOnItem +import com.niyaj.model.Address +import com.niyaj.model.CartOrder +import com.niyaj.model.CartProductItem +import com.niyaj.model.Charges +import com.niyaj.model.Customer +import com.niyaj.model.OrderPrice +import com.niyaj.model.OrderType +import com.niyaj.order.components.TextDivider +import com.niyaj.order.components.ThreeGridTexts +import com.niyaj.order.components.TwoGridTexts +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardExpandable +import com.niyaj.ui.components.StandardOutlinedChip +import com.niyaj.ui.components.StandardScaffoldNew +import com.niyaj.ui.components.TextWithIcon +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens import com.ramcosta.composedestinations.annotation.Destination -import timber.log.Timber @OptIn(ExperimentalPermissionsApi::class) -@Destination +@Destination( + route = Screens.OrderDetailsScreen +) @Composable fun OrderDetailsScreen( orderId: Int, navController: NavController, viewModel: OrderDetailsViewModel = hiltViewModel(), ) { + val context = LocalContext.current val bluetoothPermissions = @@ -123,13 +125,7 @@ fun OrderDetailsScreen( val enableBluetoothContract = rememberLauncherForActivityResult( ActivityResultContracts.StartActivityForResult() - ) { - if (it.resultCode == Activity.RESULT_OK) { - Timber.d("bluetoothLauncher", "Success") - } else { - Timber.w("bluetoothLauncher", "Failed") - } - } + ) {} // This intent will open the enable bluetooth dialog val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) @@ -235,6 +231,7 @@ fun OrderDetailsScreen( customerExpended = !customerExpended }, onClickViewDetails = { + //TODO:: Add link for customer details screen // navController.navigate(CustomerDetailsScreenDestination(it)) } ) @@ -250,6 +247,7 @@ fun OrderDetailsScreen( addressExpended = !addressExpended }, onClickViewDetails = { + //TODO:: Add link for address details screen // navController.navigate(AddressDetailsScreenDestination(addressId = it)) } ) diff --git a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/details/OrderDetailsViewModel.kt b/feature/order/src/main/java/com/niyaj/order/details/OrderDetailsViewModel.kt similarity index 84% rename from app/src/main/java/com/niyaj/poposroom/features/order/presentation/details/OrderDetailsViewModel.kt rename to feature/order/src/main/java/com/niyaj/order/details/OrderDetailsViewModel.kt index 20c88d23..c71547a7 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/order/presentation/details/OrderDetailsViewModel.kt +++ b/feature/order/src/main/java/com/niyaj/order/details/OrderDetailsViewModel.kt @@ -1,11 +1,11 @@ -package com.niyaj.poposroom.features.order.presentation.details +package com.niyaj.order.details import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.order.domain.repository.OrderRepository +import com.niyaj.data.repository.OrderRepository +import com.niyaj.ui.event.UiState import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted @@ -20,7 +20,7 @@ class OrderDetailsViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel() { - val orderId = savedStateHandle.get("orderId") ?: 0 + private val orderId = savedStateHandle.get("orderId") ?: 0 @OptIn(ExperimentalCoroutinesApi::class) val orderDetails = snapshotFlow { orderId } diff --git a/feature/order/src/test/java/com/niyaj/order/ExampleUnitTest.kt b/feature/order/src/test/java/com/niyaj/order/ExampleUnitTest.kt new file mode 100644 index 00000000..d6be1e89 --- /dev/null +++ b/feature/order/src/test/java/com/niyaj/order/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.order + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/print_order/.gitignore b/feature/print_order/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/print_order/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/print_order/build.gradle.kts b/feature/print_order/build.gradle.kts new file mode 100644 index 00000000..eae55340 --- /dev/null +++ b/feature/print_order/build.gradle.kts @@ -0,0 +1,31 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.print_order" + + ksp { + arg("compose-destinations.moduleName", "print_order") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + implementation(libs.pos.printer) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/print_order/proguard-rules.pro b/feature/print_order/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/print_order/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/print_order/src/androidTest/java/com/niyaj/print/ExampleInstrumentedTest.kt b/feature/print_order/src/androidTest/java/com/niyaj/print/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..38ddcc44 --- /dev/null +++ b/feature/print_order/src/androidTest/java/com/niyaj/print/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.print + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.print", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/print_order/src/main/AndroidManifest.xml b/feature/print_order/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/print_order/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/print_order/src/main/java/com/niyaj/print/PrintEvent.kt b/feature/print_order/src/main/java/com/niyaj/print/PrintEvent.kt new file mode 100644 index 00000000..b3425039 --- /dev/null +++ b/feature/print_order/src/main/java/com/niyaj/print/PrintEvent.kt @@ -0,0 +1,8 @@ +package com.niyaj.print + +sealed interface PrintEvent { + + data class PrintOrder(val orderId: Int): PrintEvent + + data class PrintOrders(val orderIds: List): PrintEvent +} \ No newline at end of file diff --git a/feature/print_order/src/main/java/com/niyaj/print/PrintViewModel.kt b/feature/print_order/src/main/java/com/niyaj/print/PrintViewModel.kt new file mode 100644 index 00000000..e90938e0 --- /dev/null +++ b/feature/print_order/src/main/java/com/niyaj/print/PrintViewModel.kt @@ -0,0 +1,141 @@ +package com.niyaj.print + +import androidx.lifecycle.ViewModel +import com.niyaj.common.utils.createDottedString +import com.niyaj.common.utils.toFormattedTime +import com.niyaj.data.repository.PrintRepository +import com.niyaj.model.AddOnItem +import com.niyaj.model.CartOrder +import com.niyaj.model.CartProductItem +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class PrintViewModel @Inject constructor( + private val printRepository: PrintRepository, +) : ViewModel() { + + + fun onEvent(event: PrintEvent) { + when (event) { + is PrintEvent.PrintOrder -> { + + } + + is PrintEvent.PrintOrders -> { + + } + } + } + + + private fun printRestaurantDetails(): String { +// val imagePrint = PrinterTextParserImg.bitmapToHexadecimalString(escposPrinter, resLogo) + +// var details = if (info.printResLogo) { +// "[C]$imagePrint\n\n" +// }else " \n" + + val details = "[C]--------- ORDER BILL ---------\n\n" + + return details + } + + private fun printOrderDetails(cartOrder: CartOrder): String { + var order = "" + + order += "[L]ID - [R]${cartOrder.orderId}\n" + + order += "[L]Type - [R]${cartOrder.orderType}\n" + + order += "[L]Time - [R]${System.currentTimeMillis().toString().toFormattedTime}\n" + + if (cartOrder.customer.customerPhone.isNotEmpty()) { + order += "[L]Phone - [R]${cartOrder.customer.customerPhone}\n" + } + + if (cartOrder.address.addressName.isNotEmpty()) { + order += "[L]Address - [R]${cartOrder.address.addressName}\n" + } + + return order + } + + private fun printProductDetails(orderedProduct: List): String { + var products = "" + + products += "[L]-------------------------------\n" + + products += "[L]Name[R]Qty[R]Price\n" + + products += "[L]-------------------------------\n" + + orderedProduct.forEach { + val productName = createDottedString(it.productName, 15) // info.productNameLength + + products += "[L]${productName}[R]${it.productQuantity}[R]${it.productPrice}\n" + } + + return products + } + + private fun printTotalPrice(orderPrice: Pair): String { + return "[L]-------------------------------\n" + + "[L]Total[R] Rs. ${orderPrice.first.minus(orderPrice.second)}\n" + + "[L]-------------------------------\n\n" + } + + private fun printQrCode(): String { +// return if (info.printQRCode) { +// "[C]Pay by scanning this QR code\n\n"+ +// "[L]\n" + +// "[C]${resInfo.paymentQrCode}\n\n\n" + +// "[C]Good Food, Good Mood\n\n" + +// "[L]-------------------------------\n" +// }else "" + return "" + } + + private fun printFooterInfo(): String { +// return if (info.printWelcomeText) { +// "[C]Thank you for ordering!\n" + +// "[C]For order and inquiry, Call.\n" + +// "[C]${resInfo.primaryPhone} / ${resInfo.secondaryPhone}\n\n" +// }else "" + + return "" + } + + private fun printAddOnItems(addOnItemList: List): String { + var addOnItems = "" + + if (addOnItemList.isNotEmpty()) { + addOnItems += "[L]-------------------------------\n" + for (addOnItem in addOnItemList) { + addOnItems += "[L]${addOnItem.itemName}[R]${addOnItem.itemPrice}\n" + } + + } + + return addOnItems + } + + private fun printCharges(): String { + var charges = "" + +// if (chargesList.isNotEmpty()){ +// charges += "[L]-------------------------------\n" +// for (charge in chargesList) { +// charges += "[L]${charge.chargesName}[R]${charge.chargesPrice.toString().toRupee}\n" +// } +// } + + return charges + } + + private fun printSubTotalAndDiscount(orderPrice: Pair): String { + return "[L]-------------------------------\n" + + "[L]Sub Total[R]${orderPrice.first}\n" + + "[L]Discount[R]${orderPrice.second}\n" + } +} \ No newline at end of file diff --git a/feature/print_order/src/test/java/com/niyaj/print/ExampleUnitTest.kt b/feature/print_order/src/test/java/com/niyaj/print/ExampleUnitTest.kt new file mode 100644 index 00000000..ba332c50 --- /dev/null +++ b/feature/print_order/src/test/java/com/niyaj/print/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.print + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/printer_info/.gitignore b/feature/printer_info/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/printer_info/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/printer_info/build.gradle.kts b/feature/printer_info/build.gradle.kts new file mode 100644 index 00000000..83fe253e --- /dev/null +++ b/feature/printer_info/build.gradle.kts @@ -0,0 +1,31 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.printer_info" + + ksp { + arg("compose-destinations.moduleName", "printer_info") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.permissions) + implementation(libs.pos.printer) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/printer_info/proguard-rules.pro b/feature/printer_info/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/printer_info/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/printer_info/src/androidTest/java/com/niyaj/printer_info/ExampleInstrumentedTest.kt b/feature/printer_info/src/androidTest/java/com/niyaj/printer_info/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..91222e54 --- /dev/null +++ b/feature/printer_info/src/androidTest/java/com/niyaj/printer_info/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.printer_info + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.printer_info", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/printer_info/src/main/AndroidManifest.xml b/feature/printer_info/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/printer_info/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/printer_info/src/main/java/com/niyaj/printer_info/PrinterInfoScreen.kt b/feature/printer_info/src/main/java/com/niyaj/printer_info/PrinterInfoScreen.kt new file mode 100644 index 00000000..0a144fc3 --- /dev/null +++ b/feature/printer_info/src/main/java/com/niyaj/printer_info/PrinterInfoScreen.kt @@ -0,0 +1,438 @@ +package com.niyaj.printer_info + +import android.Manifest +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothManager +import android.content.Intent +import android.os.Build +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import androidx.compose.animation.Crossfade +import androidx.compose.foundation.background +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.InsertLink +import androidx.compose.material.icons.filled.Notes +import androidx.compose.material.icons.filled.Print +import androidx.compose.material.icons.outlined.Edit +import androidx.compose.material3.Card +import androidx.compose.material3.Divider +import androidx.compose.material3.ElevatedCard +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.rememberMultiplePermissionsState +import com.niyaj.common.utils.toPrettyDate +import com.niyaj.common.utils.toSafeString +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_INFO_NOTES_FOUR +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_INFO_NOTES_ONE +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_INFO_NOTES_THREE +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_INFO_NOTES_TWO +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_NOT_AVAIlABLE +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_SCREEN_TITLE +import com.niyaj.data.utils.PrinterInfoTestTags.UPDATE_PRINTER_INFO +import com.niyaj.designsystem.theme.LightColor9 +import com.niyaj.designsystem.theme.ProfilePictureSizeMedium +import com.niyaj.designsystem.theme.ProfilePictureSizeSmall +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.printer_info.destinations.UpdatePrinterInfoScreenDestination +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.NoteCard +import com.niyaj.ui.components.StandardChip +import com.niyaj.ui.components.StandardOutlinedChip +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.components.TextWithIcon +import com.niyaj.ui.components.TwoGridText +import com.niyaj.ui.components.drawAnimatedBorder +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.ramcosta.composedestinations.navigation.navigate +import com.ramcosta.composedestinations.result.NavResult +import com.ramcosta.composedestinations.result.ResultRecipient +import kotlinx.coroutines.launch + +@OptIn(ExperimentalPermissionsApi::class) +@Composable +@RootNavGraph(start = true) +@Destination( + route = Screens.PrinterInfoScreen +) +fun PrinterInfoScreen( + navController: NavController, + viewModel: PrinterInfoViewModel = hiltViewModel(), + resultRecipient: ResultRecipient, +) { + val context = LocalContext.current + val snackbarHostState = remember { SnackbarHostState() } + + val bluetoothPermissions = + // Checks if the device has Android 12 or above + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + rememberMultiplePermissionsState( + permissions = listOf( + Manifest.permission.BLUETOOTH, + Manifest.permission.BLUETOOTH_ADMIN, + Manifest.permission.BLUETOOTH_CONNECT, + Manifest.permission.BLUETOOTH_SCAN, + ) + ) + } else { + rememberMultiplePermissionsState( + permissions = listOf( + Manifest.permission.BLUETOOTH, + Manifest.permission.BLUETOOTH_ADMIN, + ) + ) + } + + val enableBluetoothContract = rememberLauncherForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) {} + + // This intent will open the enable bluetooth dialog + val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) + + val bluetoothManager = remember { + context.getSystemService(BluetoothManager::class.java) + } + + val bluetoothAdapter: BluetoothAdapter? = remember { + bluetoothManager.adapter + } + + LaunchedEffect(key1 = Unit) { + if (bluetoothAdapter?.isEnabled == false) { + enableBluetoothContract.launch(enableBluetoothIntent) + } + + if (!bluetoothPermissions.allPermissionsGranted) { + bluetoothPermissions.launchMultiplePermissionRequest() + } + } + + + val lazyListState = rememberLazyListState() + val scope = rememberCoroutineScope() + + val uiState = viewModel.info.collectAsStateWithLifecycle().value + val printers = viewModel.printers.collectAsStateWithLifecycle().value + + resultRecipient.onNavResult { result -> + when (result) { + is NavResult.Canceled -> {} + is NavResult.Value -> { + scope.launch { + snackbarHostState.showSnackbar( + result.value + ) + } + } + } + } + + StandardScaffold( + navController = navController, + snackbarHostState = snackbarHostState, + title = PRINTER_SCREEN_TITLE, + showBottomBar = false, + navActions = { + IconButton( + onClick = { + navController.navigate(UpdatePrinterInfoScreenDestination()) + } + ) { + Icon( + imageVector = Icons.Outlined.Edit, + contentDescription = "Edit Printer Information" + ) + } + }, + floatingActionButton = {}, + selectionCount = 0, + ) { + Crossfade( + targetState = uiState, + label = "Printer Information State" + ) { state -> + when (state) { + is UiState.Loading -> LoadingIndicator() + + is UiState.Empty -> { + ItemNotAvailable( + text = PRINTER_NOT_AVAIlABLE, + buttonText = UPDATE_PRINTER_INFO, + icon = Icons.Default.Edit, + onClick = { +// navController.navigate(UpdatePrinterInfoDestination()) + } + ) + } + + is UiState.Success -> { + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceSmall), + state = lazyListState, + verticalArrangement = Arrangement.spacedBy(SpaceSmall) + ) { + item("Notes") { + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(SpaceSmall) + ) { + Spacer(modifier = Modifier.height(SpaceSmall)) + + NoteCard(text = PRINTER_INFO_NOTES_ONE) + + NoteCard(text = PRINTER_INFO_NOTES_TWO) + + NoteCard(text = PRINTER_INFO_NOTES_THREE) + + NoteCard(text = PRINTER_INFO_NOTES_FOUR) + } + } + + item("Printers") { + Column( + modifier = Modifier + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(SpaceSmall), + ) { + Text( + text = "Printers", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold, + ) + + if (printers.isNotEmpty()) { + printers.forEach { data -> + Card( + modifier = Modifier + .fillMaxWidth(), + shape = RoundedCornerShape(SpaceMini), +// backgroundColor = LightColor7 + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceSmall), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column( + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy( + SpaceMini + ) + ) { + TextWithIcon( + text = data.name, + icon = Icons.Default.Notes + ) + + TextWithIcon( + text = data.address, + icon = Icons.Default.InsertLink + ) + } + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy( + SpaceSmall + ) + ) { + StandardOutlinedChip( + text = "Test Print", + onClick = viewModel::testPrint + ) + + StandardChip( + text = if (data.connected) "Connected" else "Connect", +// icon = if (!data.connected) Icons.Default.BluetoothConnected else Icons.Default.BluetoothDisabled, + isPrimary = data.connected, + isClickable = !data.connected, + onClick = { + viewModel.connectPrinter(data.address) + } + ) + } + + } + } + } + } else { + Text( + text = "Bluetooth printer is not available on this device", + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.error, + ) + } + } + } + + item("Printer Information") { + ElevatedCard( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = SpaceSmall), + shape = RoundedCornerShape(SpaceSmall), +// backgroundColor = LightColor7 + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceSmall), + verticalArrangement = Arrangement.spacedBy(SpaceSmall), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Spacer(modifier = Modifier.height(SpaceSmall)) + + Box( + modifier = Modifier + .size(ProfilePictureSizeMedium) + .background(LightColor9, CircleShape) + .clip(CircleShape) + .drawAnimatedBorder( + 1.dp, + CircleShape, + durationMillis = 2000 + ), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Default.Print, + contentDescription = "Printer Info", + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier + .size(ProfilePictureSizeSmall) + .align(Alignment.Center) + ) + } + + Text( + text = "Printer Information", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold, + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Printer DPI", + textTwo = state.data.printerDpi.toString() + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Printer Width", + textTwo = "${state.data.printerWidth} mm" + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Printer NBR Lines", + textTwo = state.data.printerNbrLines.toString() + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Product Name Length", + textTwo = state.data.productNameLength.toString() + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Product Report Limit", + textTwo = state.data.productWiseReportLimit.toString() + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Address Report Limit", + textTwo = state.data.addressWiseReportLimit.toString() + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Customer Report Limit", + textTwo = state.data.customerWiseReportLimit.toString() + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Print QR Code", + textTwo = state.data.printQRCode.toSafeString + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Print Restaurant Logo", + textTwo = state.data.printResLogo.toSafeString + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Print Welcome Text", + textTwo = state.data.printWelcomeText.toSafeString + ) + + Divider(modifier = Modifier.fillMaxWidth()) + + TwoGridText( + textOne = "Last Updated", + textTwo = (state.data.updatedAt + ?: state.data.createdAt).toPrettyDate() + ) + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/feature/printer_info/src/main/java/com/niyaj/printer_info/PrinterInfoViewModel.kt b/feature/printer_info/src/main/java/com/niyaj/printer_info/PrinterInfoViewModel.kt new file mode 100644 index 00000000..b0b5abec --- /dev/null +++ b/feature/printer_info/src/main/java/com/niyaj/printer_info/PrinterInfoViewModel.kt @@ -0,0 +1,53 @@ +package com.niyaj.printer_info + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.niyaj.data.repository.PrinterRepository +import com.niyaj.domain.utils.BluetoothPrinter +import com.niyaj.ui.event.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class PrinterInfoViewModel @Inject constructor( + repository: PrinterRepository, + private val bluetoothPrinter: BluetoothPrinter, +) : ViewModel() { + + val info = bluetoothPrinter.info + .mapLatest { + if (it.printerId.isEmpty()) { + UiState.Empty + } else { + UiState.Success(it) + } + } + .stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + UiState.Loading + ) + + val printers = bluetoothPrinter.getBluetoothPrinters().stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + emptyList() + ) + + fun connectPrinter(address: String) { + viewModelScope.launch { + bluetoothPrinter.connectBluetoothPrinter(address) + } + } + + fun testPrint() { + viewModelScope.launch { + bluetoothPrinter.printTestData() + } + } + +} \ No newline at end of file diff --git a/feature/printer_info/src/main/java/com/niyaj/printer_info/add_edit/UpdatePrinterInfoEvent.kt b/feature/printer_info/src/main/java/com/niyaj/printer_info/add_edit/UpdatePrinterInfoEvent.kt new file mode 100644 index 00000000..8ea3923c --- /dev/null +++ b/feature/printer_info/src/main/java/com/niyaj/printer_info/add_edit/UpdatePrinterInfoEvent.kt @@ -0,0 +1,19 @@ +package com.niyaj.printer_info.add_edit + +sealed interface UpdatePrinterInfoEvent { + + data class PrinterDpiChanged(val printerDpi : String) : UpdatePrinterInfoEvent + data class PrinterWidthChanged(val printerWidth : String) : UpdatePrinterInfoEvent + data class PrinterNbrLinesChanged(val printerNbrLines : String) : UpdatePrinterInfoEvent + data class ProductNameLengthChanged(val length : String) : UpdatePrinterInfoEvent + + data class ProductReportLimitChanged(val limit : String) : UpdatePrinterInfoEvent + data class AddressReportLimitChanged(val limit : String) : UpdatePrinterInfoEvent + data class CustomerReportLimitChanged(val limit : String) : UpdatePrinterInfoEvent + + data object PrintQrCodeChanged : UpdatePrinterInfoEvent + data object PrintResLogoChanged : UpdatePrinterInfoEvent + data object PrintWelcomeTextChanged : UpdatePrinterInfoEvent + + data object UpdatePrinterInfo : UpdatePrinterInfoEvent +} \ No newline at end of file diff --git a/feature/printer_info/src/main/java/com/niyaj/printer_info/add_edit/UpdatePrinterInfoScreen.kt b/feature/printer_info/src/main/java/com/niyaj/printer_info/add_edit/UpdatePrinterInfoScreen.kt new file mode 100644 index 00000000..645d2c21 --- /dev/null +++ b/feature/printer_info/src/main/java/com/niyaj/printer_info/add_edit/UpdatePrinterInfoScreen.kt @@ -0,0 +1,248 @@ +package com.niyaj.printer_info.add_edit + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.DensityMedium +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.Margin +import androidx.compose.material.icons.filled.Receipt +import androidx.compose.material.icons.filled.ReceiptLong +import androidx.compose.material.icons.filled.ViewHeadline +import androidx.compose.material.icons.filled.WidthNormal +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import com.niyaj.data.utils.PrinterInfoTestTags.ADDRESS_REPORT_LIMIT_FIELD +import com.niyaj.data.utils.PrinterInfoTestTags.ADDRESS_REPORT_LIMIT_MESSAGE +import com.niyaj.data.utils.PrinterInfoTestTags.CUSTOMER_REPORT_LIMIT_FIELD +import com.niyaj.data.utils.PrinterInfoTestTags.CUSTOMER_REPORT_LIMIT_MESSAGE +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_DPI_FIELD +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_DPI_MESSAGE +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_NBR_LINES_FIELD +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_NBR_LINES_MESSAGE +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_PRODUCT_NAME_LENGTH_FIELD +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_PRODUCT_NAME_LENGTH_MESSAGE +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_WIDTH_FIELD +import com.niyaj.data.utils.PrinterInfoTestTags.PRINTER_WIDTH_MESSAGE +import com.niyaj.data.utils.PrinterInfoTestTags.PRINT_LOGO_IN_BILL +import com.niyaj.data.utils.PrinterInfoTestTags.PRINT_QR_CODE_IN_BILL +import com.niyaj.data.utils.PrinterInfoTestTags.PRINT_WELCOME_TEXT_IN_BILL +import com.niyaj.data.utils.PrinterInfoTestTags.PRODUCT_REPORT_LIMIT_FIELD +import com.niyaj.data.utils.PrinterInfoTestTags.PRODUCT_REPORT_LIMIT_MESSAGE +import com.niyaj.data.utils.PrinterInfoTestTags.UPDATE_PRINTER_BUTTON +import com.niyaj.data.utils.PrinterInfoTestTags.UPDATE_PRINTER_INFO +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardCheckboxWithText +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.result.ResultBackNavigator + +@Destination( + route = Screens.UpdatePrinterInfoScreen +) +@Composable +fun UpdatePrinterInfoScreen( + navController: NavController, + resultBackNavigator: ResultBackNavigator, + viewModel: UpdatePrinterInfoViewModel = hiltViewModel(), +) { + val state = rememberLazyListState() + + val dpiError = viewModel.dpiError.collectAsStateWithLifecycle().value + val widthError = viewModel.widthError.collectAsStateWithLifecycle().value + val nbrError = viewModel.nbrError.collectAsStateWithLifecycle().value + val nameLengthError = viewModel.nameLengthError.collectAsStateWithLifecycle().value + val productLimitError = viewModel.productLimitError.collectAsStateWithLifecycle().value + val addressLimitError = viewModel.addressLimitError.collectAsStateWithLifecycle().value + val customerLimitError = viewModel.customerLimitError.collectAsStateWithLifecycle().value + + val hasError = viewModel.hasError.collectAsStateWithLifecycle().value + + LaunchedEffect(key1 = true) { + viewModel.eventFlow.collect { event -> + when (event) { + is UiEvent.OnSuccess -> { + resultBackNavigator.navigateBack(event.successMessage) + } + + is UiEvent.OnError -> { + resultBackNavigator.navigateBack(event.errorMessage) + } + + } + } + } + + StandardScaffoldWithOutDrawer( + title = UPDATE_PRINTER_INFO, + onBackClick = { + navController.navigateUp() + }, + showBottomBar = !hasError, + bottomBar = { + StandardButton( + modifier = Modifier + .fillMaxWidth() + .testTag(UPDATE_PRINTER_BUTTON) + .padding(horizontal = SpaceSmallMax), + enabled = !hasError, + text = UPDATE_PRINTER_INFO, + icon = Icons.Default.Edit, + onClick = { + viewModel.onEvent(UpdatePrinterInfoEvent.UpdatePrinterInfo) + } + ) + } + ) { + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceSmall), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(SpaceSmall), + state = state, + ) { + item { + StandardOutlinedTextField( + value = viewModel.state.printerDpi.toString(), + leadingIcon = Icons.Default.DensityMedium, + label = PRINTER_DPI_FIELD, + isError = dpiError != null, + errorText = dpiError, + message = PRINTER_DPI_MESSAGE, + onValueChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.PrinterDpiChanged(it)) + } + ) + } + + item { + StandardOutlinedTextField( + value = viewModel.state.printerWidth.toString(), + label = PRINTER_WIDTH_FIELD, + leadingIcon = Icons.Default.WidthNormal, + isError = widthError != null, + errorText = widthError, + message = PRINTER_WIDTH_MESSAGE, + onValueChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.PrinterWidthChanged(it)) + } + ) + } + + item { + StandardOutlinedTextField( + value = viewModel.state.printerNbrLines.toString(), + label = PRINTER_NBR_LINES_FIELD, + leadingIcon = Icons.Default.ViewHeadline, + isError = nbrError != null, + errorText = nbrError, + message = PRINTER_NBR_LINES_MESSAGE, + onValueChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.PrinterNbrLinesChanged(it)) + } + ) + } + + item { + StandardOutlinedTextField( + value = viewModel.state.productNameLength.toString(), + label = PRINTER_PRODUCT_NAME_LENGTH_FIELD, + leadingIcon = Icons.Default.Margin, + isError = nameLengthError != null, + errorText = nameLengthError, + message = PRINTER_PRODUCT_NAME_LENGTH_MESSAGE, + onValueChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.ProductNameLengthChanged(it)) + } + ) + } + + item { + StandardOutlinedTextField( + value = viewModel.state.productWiseReportLimit.toString(), + label = PRODUCT_REPORT_LIMIT_FIELD, + leadingIcon = Icons.Default.ReceiptLong, + isError = productLimitError != null, + errorText = productLimitError, + message = PRODUCT_REPORT_LIMIT_MESSAGE, + onValueChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.ProductReportLimitChanged(it)) + } + ) + } + + item { + StandardOutlinedTextField( + value = viewModel.state.addressWiseReportLimit.toString(), + label = ADDRESS_REPORT_LIMIT_FIELD, + leadingIcon = Icons.Default.Receipt, + isError = addressLimitError != null, + errorText = addressLimitError, + message = ADDRESS_REPORT_LIMIT_MESSAGE, + onValueChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.AddressReportLimitChanged(it)) + } + ) + } + + item { + StandardOutlinedTextField( + value = viewModel.state.customerWiseReportLimit.toString(), + label = CUSTOMER_REPORT_LIMIT_FIELD, + leadingIcon = Icons.Default.Receipt, + isError = customerLimitError != null, + errorText = customerLimitError, + message = CUSTOMER_REPORT_LIMIT_MESSAGE, + onValueChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.CustomerReportLimitChanged(it)) + } + ) + } + + item { + StandardCheckboxWithText( + text = PRINT_QR_CODE_IN_BILL, + checked = viewModel.state.printQRCode, + onCheckedChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.PrintQrCodeChanged) + } + ) + } + + item { + StandardCheckboxWithText( + text = PRINT_LOGO_IN_BILL, + checked = viewModel.state.printResLogo, + onCheckedChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.PrintResLogoChanged) + } + ) + } + + item { + StandardCheckboxWithText( + text = PRINT_WELCOME_TEXT_IN_BILL, + checked = viewModel.state.printWelcomeText, + onCheckedChange = { + viewModel.onEvent(UpdatePrinterInfoEvent.PrintWelcomeTextChanged) + } + ) + } + } + } +} \ No newline at end of file diff --git a/feature/printer_info/src/main/java/com/niyaj/printer_info/add_edit/UpdatePrinterInfoViewModel.kt b/feature/printer_info/src/main/java/com/niyaj/printer_info/add_edit/UpdatePrinterInfoViewModel.kt new file mode 100644 index 00000000..37702987 --- /dev/null +++ b/feature/printer_info/src/main/java/com/niyaj/printer_info/add_edit/UpdatePrinterInfoViewModel.kt @@ -0,0 +1,214 @@ +package com.niyaj.printer_info.add_edit + +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.snapshotFlow +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.safeFloat +import com.niyaj.common.utils.safeInt +import com.niyaj.data.repository.PrinterRepository +import com.niyaj.data.repository.validation.PrinterValidationRepository +import com.niyaj.model.Printer +import com.niyaj.ui.utils.UiEvent +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import javax.inject.Inject + +@OptIn(ExperimentalCoroutinesApi::class) +@HiltViewModel +class UpdatePrinterInfoViewModel @Inject constructor( + private val repository: PrinterRepository, + private val validationRepository: PrinterValidationRepository, +) : ViewModel() { + + private val _state = mutableStateOf(Printer()) + val state: Printer + get() = _state.value + + private val _eventFlow = MutableSharedFlow() + val eventFlow = _eventFlow.asSharedFlow() + + + init { + getPrinterInfo() + } + + val dpiError = snapshotFlow { _state.value.printerDpi } + .mapLatest { + validationRepository.validatePrinterDpi(it).errorMessage + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + + val widthError = snapshotFlow { _state.value.printerWidth } + .mapLatest { + validationRepository.validatePrinterWidth(it).errorMessage + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + val nbrError = snapshotFlow { _state.value.printerNbrLines } + .mapLatest { + validationRepository.validatePrinterNbrLines(it).errorMessage + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + + val nameLengthError = snapshotFlow { _state.value.productNameLength } + .mapLatest { + validationRepository.validateProductNameLength(it).errorMessage + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + + val productLimitError = snapshotFlow { _state.value.productWiseReportLimit } + .mapLatest { + validationRepository.validateProductReportLimit(it).errorMessage + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + + val addressLimitError = snapshotFlow { _state.value.addressWiseReportLimit } + .mapLatest { + validationRepository.validateAddressReportLimit(it).errorMessage + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + + val customerLimitError = snapshotFlow { _state.value.customerWiseReportLimit } + .mapLatest { + validationRepository.validateCustomerReportLimit(it).errorMessage + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + + val hasError = combine( + dpiError, + widthError, + nbrError, + nameLengthError, + productLimitError, + addressLimitError, + customerLimitError + ) { list -> + list.any { + it != null + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), true) + + + fun onEvent(event: UpdatePrinterInfoEvent) { + when (event) { + is UpdatePrinterInfoEvent.PrinterDpiChanged -> { + _state.value = _state.value.copy( + printerDpi = event.printerDpi.safeInt() + ) + } + + is UpdatePrinterInfoEvent.PrinterNbrLinesChanged -> { + _state.value = _state.value.copy( + printerNbrLines = event.printerNbrLines.safeInt() + ) + } + + is UpdatePrinterInfoEvent.PrinterWidthChanged -> { + _state.value = _state.value.copy( + printerWidth = event.printerWidth.safeFloat() + ) + } + + is UpdatePrinterInfoEvent.ProductNameLengthChanged -> { + _state.value = _state.value.copy( + productNameLength = event.length.safeInt() + ) + } + + is UpdatePrinterInfoEvent.ProductReportLimitChanged -> { + _state.value = _state.value.copy( + productWiseReportLimit = event.limit.safeInt() + ) + } + + is UpdatePrinterInfoEvent.AddressReportLimitChanged -> { + _state.value = _state.value.copy( + addressWiseReportLimit = event.limit.safeInt() + ) + } + + is UpdatePrinterInfoEvent.CustomerReportLimitChanged -> { + _state.value = _state.value.copy( + customerWiseReportLimit = event.limit.safeInt() + ) + } + + is UpdatePrinterInfoEvent.PrintQrCodeChanged -> { + _state.value = _state.value.copy( + printQRCode = !_state.value.printQRCode + ) + } + + is UpdatePrinterInfoEvent.PrintResLogoChanged -> { + _state.value = _state.value.copy( + printResLogo = !_state.value.printResLogo + ) + } + + is UpdatePrinterInfoEvent.PrintWelcomeTextChanged -> { + _state.value = _state.value.copy( + printWelcomeText = !_state.value.printWelcomeText + ) + } + + is UpdatePrinterInfoEvent.UpdatePrinterInfo -> { + updatePrinterInfo() + } + + } + } + + private fun updatePrinterInfo() { + viewModelScope.launch { + if (!hasError.value) { + when (repository.addOrUpdatePrinterInfo(_state.value)) { + is Resource.Success -> { + _eventFlow.emit(UiEvent.OnSuccess("Printer info updated successfully")) + } + + is Resource.Error -> { + _eventFlow.emit(UiEvent.OnError("Unable to update printer info")) + } + } + } else return@launch + } + } + + private fun getPrinterInfo() { + viewModelScope.launch { + repository.getPrinter().let { + _state.value = _state.value.copy( + printerId = it.printerId, + printerDpi = it.printerDpi, + printerWidth = it.printerWidth, + printerNbrLines = it.printerNbrLines, + productNameLength = it.productNameLength, + productWiseReportLimit = it.productWiseReportLimit, + addressWiseReportLimit = it.addressWiseReportLimit, + customerWiseReportLimit = it.customerWiseReportLimit, + printQRCode = it.printQRCode, + printResLogo = it.printResLogo, + printWelcomeText = it.printWelcomeText, + createdAt = it.createdAt, + updatedAt = it.updatedAt + ) + } + } + } + +} \ No newline at end of file diff --git a/feature/printer_info/src/test/java/com/niyaj/printer_info/ExampleUnitTest.kt b/feature/printer_info/src/test/java/com/niyaj/printer_info/ExampleUnitTest.kt new file mode 100644 index 00000000..faed42d7 --- /dev/null +++ b/feature/printer_info/src/test/java/com/niyaj/printer_info/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.printer_info + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/product/.gitignore b/feature/product/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/product/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/product/build.gradle.kts b/feature/product/build.gradle.kts new file mode 100644 index 00000000..0ebaec8e --- /dev/null +++ b/feature/product/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.product" + + ksp { + arg("compose-destinations.moduleName", "product") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/product/proguard-rules.pro b/feature/product/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/product/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/product/src/androidTest/java/com/niyaj/product/ExampleInstrumentedTest.kt b/feature/product/src/androidTest/java/com/niyaj/product/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..9a445290 --- /dev/null +++ b/feature/product/src/androidTest/java/com/niyaj/product/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.product + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.product", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/product/src/main/AndroidManifest.xml b/feature/product/src/main/AndroidManifest.xml new file mode 100644 index 00000000..44008a43 --- /dev/null +++ b/feature/product/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/ProductScreen.kt b/feature/product/src/main/java/com/niyaj/product/ProductScreen.kt similarity index 71% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/ProductScreen.kt rename to feature/product/src/main/java/com/niyaj/product/ProductScreen.kt index f60f2037..ca72a2b1 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/ProductScreen.kt +++ b/feature/product/src/main/java/com/niyaj/product/ProductScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.presentation +package com.niyaj.product import androidx.activity.compose.BackHandler import androidx.compose.foundation.BorderStroke @@ -7,28 +7,20 @@ import androidx.compose.foundation.border import androidx.compose.foundation.combinedClickable 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.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.CalendarMonth -import androidx.compose.material.icons.filled.Category import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.TurnedInNot import androidx.compose.material3.AlertDialog import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.ElevatedCard -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FabPosition import androidx.compose.material3.ListItem import androidx.compose.material3.MaterialTheme @@ -40,51 +32,51 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.category.domain.utils.CategoryConstants -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.ItemNotAvailable -import com.niyaj.poposroom.features.common.components.LoadingIndicator -import com.niyaj.poposroom.features.common.components.NoteText -import com.niyaj.poposroom.features.common.components.ScaffoldNavActions -import com.niyaj.poposroom.features.common.components.StandardFAB -import com.niyaj.poposroom.features.common.components.StandardScaffold -import com.niyaj.poposroom.features.common.event.UiState -import com.niyaj.poposroom.features.common.ui.theme.SpaceLarge -import com.niyaj.poposroom.features.common.ui.theme.SpaceMini -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.isScrolled -import com.niyaj.poposroom.features.common.utils.toPrettyDate -import com.niyaj.poposroom.features.common.utils.toRupee -import com.niyaj.poposroom.features.destinations.AddEditProductScreenDestination -import com.niyaj.poposroom.features.product.domain.model.Product -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.CREATE_NEW_PRODUCT -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.DELETE_PRODUCT_MESSAGE -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.DELETE_PRODUCT_TITLE -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.NO_ITEMS_IN_PRODUCT -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_NOT_AVAIlABLE -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_SCREEN_TITLE -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_SEARCH_PLACEHOLDER -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_TAG +import com.niyaj.common.utils.toPrettyDate +import com.niyaj.common.utils.toRupee +import com.niyaj.data.utils.ProductTestTags.CREATE_NEW_PRODUCT +import com.niyaj.data.utils.ProductTestTags.DELETE_PRODUCT_MESSAGE +import com.niyaj.data.utils.ProductTestTags.DELETE_PRODUCT_TITLE +import com.niyaj.data.utils.ProductTestTags.NO_ITEMS_IN_PRODUCT +import com.niyaj.data.utils.ProductTestTags.PRODUCT_NOT_AVAIlABLE +import com.niyaj.data.utils.ProductTestTags.PRODUCT_SCREEN_TITLE +import com.niyaj.data.utils.ProductTestTags.PRODUCT_SEARCH_PLACEHOLDER +import com.niyaj.data.utils.ProductTestTags.PRODUCT_TAG +import com.niyaj.designsystem.theme.SpaceLarge +import com.niyaj.designsystem.theme.SpaceMini +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.Product +import com.niyaj.product.destinations.AddEditProductScreenDestination +import com.niyaj.ui.components.CategoriesData +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.NoteText +import com.niyaj.ui.components.ScaffoldNavActions +import com.niyaj.ui.components.StandardFAB +import com.niyaj.ui.components.StandardScaffold +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent +import com.niyaj.ui.utils.isScrolled import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph import com.ramcosta.composedestinations.navigation.navigate import com.ramcosta.composedestinations.result.NavResult import com.ramcosta.composedestinations.result.ResultRecipient import kotlinx.coroutines.launch -@Destination +@RootNavGraph(start = true) +@Destination( + route = Screens.ProductsScreen +) @Composable fun ProductScreen( navController: NavController, @@ -115,14 +107,12 @@ fun ProductScreen( LaunchedEffect(key1 = event) { event?.let { data -> when (data) { - is UiEvent.IsLoading -> {} is UiEvent.OnError -> { scope.launch { snackbarState.showSnackbar(data.errorMessage) } viewModel.deselectItems() } - is UiEvent.OnSuccess -> { scope.launch { snackbarState.showSnackbar(data.successMessage) @@ -163,10 +153,22 @@ fun ProductScreen( StandardScaffold( navController = navController, - snackbarHostState = snackbarState, title = if (selectedItems.isEmpty()) PRODUCT_SCREEN_TITLE else "${selectedItems.size} Selected", - selectionCount = selectedItems.size, - showBackButton = showSearchBar, + floatingActionButton = { + StandardFAB( + showScrollToTop = lazyListState.isScrolled, + fabText = CREATE_NEW_PRODUCT, + fabVisible = (showFab && selectedItems.isEmpty() && !showSearchBar), + onFabClick = { + navController.navigate(AddEditProductScreenDestination()) + }, + onClickScroll = { + scope.launch { + lazyListState.animateScrollToItem(0) + } + } + ) + }, navActions = { ScaffoldNavActions( placeholderText = PRODUCT_SEARCH_PLACEHOLDER, @@ -189,31 +191,12 @@ fun ProductScreen( onSearchTextChanged = viewModel::searchTextChanged ) }, - floatingActionButton = { - StandardFAB( - showScrollToTop = lazyListState.isScrolled, - fabText = CREATE_NEW_PRODUCT, - fabVisible = (showFab && selectedItems.isEmpty() && !showSearchBar), - onFabClick = { - navController.navigate(AddEditProductScreenDestination()) - }, - onClickScroll = { - scope.launch { - lazyListState.animateScrollToItem(0) - } - } - ) - }, fabPosition = if (lazyListState.isScrolled) FabPosition.End else FabPosition.Center, - onEditClick = { - navController.navigate(AddEditProductScreenDestination(selectedItems.first())) - }, - onDeleteClick = { - openDialog.value = true - }, + selectionCount = selectedItems.size, + showBackButton = showSearchBar, onDeselect = viewModel::deselectItems, - onSelectAllClick = viewModel::selectAllItems, onBackClick = viewModel::closeSearchBar, + snackbarHostState = snackbarState, ) { _ -> Column( modifier = Modifier @@ -306,75 +289,6 @@ fun ProductScreen( } -@Composable -fun CategoriesData( - categories: List, - selectedCategory: Int, - onSelect: (Int) -> Unit, -) { - LazyRow( - modifier = Modifier.fillMaxWidth() - ) { - items(categories) { category -> - CategoryData( - item = category, - doesSelected = { - selectedCategory == it - } , - onClick = onSelect, - ) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun CategoryData( - modifier: Modifier = Modifier, - item: Category, - doesSelected: (Int) -> Boolean, - onClick: (Int) -> Unit, - selectedColor: Color = MaterialTheme.colorScheme.secondaryContainer, - unselectedColor: Color = MaterialTheme.colorScheme.surface -) { - val color = if (doesSelected(item.categoryId)) selectedColor else unselectedColor - - ElevatedCard( - modifier = modifier - .testTag(CategoryConstants.CATEGORY_ITEM_TAG.plus(item.categoryId)) - .padding(SpaceSmall), - onClick = { - onClick(item.categoryId) - }, - colors = CardDefaults.elevatedCardColors( - containerColor = color - ) - ) { - Row( - modifier = Modifier - .fillMaxWidth() - .padding(SpaceSmall), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - CircularBox( - icon = Icons.Default.Category, - doesSelected = doesSelected(item.categoryId), - size = 25.dp, - text = item.categoryName - ) - - Spacer(modifier = Modifier.width(SpaceSmallMax)) - - Text( - text = item.categoryName, - style = MaterialTheme.typography.labelLarge, - fontWeight = FontWeight.SemiBold, - ) - } - } -} - @OptIn(ExperimentalFoundationApi::class) @Composable fun ProductData( diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/ProductViewModel.kt b/feature/product/src/main/java/com/niyaj/product/ProductViewModel.kt similarity index 66% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/ProductViewModel.kt rename to feature/product/src/main/java/com/niyaj/product/ProductViewModel.kt index 35c9d731..1d452f75 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/ProductViewModel.kt +++ b/feature/product/src/main/java/com/niyaj/product/ProductViewModel.kt @@ -1,16 +1,14 @@ -package com.niyaj.poposroom.features.product.presentation +package com.niyaj.product import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.common.event.BaseViewModel -import com.niyaj.poposroom.features.common.event.UiState.Empty -import com.niyaj.poposroom.features.common.event.UiState.Loading -import com.niyaj.poposroom.features.common.event.UiState.Success -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.product.domain.repository.ProductRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.data.repository.ProductRepository +import com.niyaj.ui.event.BaseViewModel +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -28,7 +26,7 @@ import javax.inject.Inject class ProductViewModel @Inject constructor( private val productRepository: ProductRepository, @Dispatcher(PoposDispatchers.IO) - private val ioDispatcher: CoroutineDispatcher + private val ioDispatcher: CoroutineDispatcher, ) : BaseViewModel() { override var totalItems: List = emptyList() @@ -40,20 +38,19 @@ class ProductViewModel @Inject constructor( @OptIn(ExperimentalCoroutinesApi::class) val products = _text.combine(_selectedCategory) { text, category -> productRepository.getAllProduct(text, category) - } - .flatMapLatest { it -> - it.map { items -> - totalItems = items.map { it.productId } + }.flatMapLatest { it -> + it.map { items -> + totalItems = items.map { it.productId } - if (items.isEmpty()) { - Empty - } else Success(items) - } - }.stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(5_000), - initialValue = Loading - ) + if (items.isEmpty()) { + UiState.Empty + } else UiState.Success(items) + } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = UiState.Loading + ) val categories = productRepository.getAllCategory().stateIn( scope = viewModelScope, @@ -83,7 +80,7 @@ class ProductViewModel @Inject constructor( viewModelScope.launch { if (_selectedCategory.value == categoryId) { _selectedCategory.value = 0 - }else { + } else { _selectedCategory.value = categoryId } } diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductEvent.kt b/feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductEvent.kt similarity index 69% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductEvent.kt rename to feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductEvent.kt index 9e24e062..3697d7e8 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductEvent.kt +++ b/feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductEvent.kt @@ -1,6 +1,6 @@ -package com.niyaj.poposroom.features.product.presentation.add_edit +package com.niyaj.product.add_edit -import com.niyaj.poposroom.features.category.domain.model.Category +import com.niyaj.model.Category sealed interface AddEditProductEvent { @@ -12,7 +12,7 @@ sealed interface AddEditProductEvent { data class ProductDescChanged(val productDesc: String): AddEditProductEvent - object ProductAvailabilityChanged: AddEditProductEvent + data object ProductAvailabilityChanged: AddEditProductEvent data class AddOrUpdateProduct(val productId: Int = 0): AddEditProductEvent } \ No newline at end of file diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductScreen.kt b/feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductScreen.kt similarity index 85% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductScreen.kt rename to feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductScreen.kt index 31be64bc..fc2d62d3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductScreen.kt +++ b/feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductScreen.kt @@ -1,5 +1,6 @@ -package com.niyaj.poposroom.features.product.presentation.add_edit +package com.niyaj.product.add_edit +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -46,32 +47,33 @@ import androidx.compose.ui.window.PopupProperties import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavController -import com.niyaj.poposroom.features.common.components.CircularBox -import com.niyaj.poposroom.features.common.components.StandardButton -import com.niyaj.poposroom.features.common.components.StandardOutlinedTextField -import com.niyaj.poposroom.features.common.components.StandardScaffoldWithOutDrawer -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmallMax -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.destinations.CategoryScreenDestination -import com.niyaj.poposroom.features.employee_payment.domain.utils.PaymentScreenTags -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.ADD_EDIT_PRODUCT_BUTTON -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.CREATE_NEW_PRODUCT -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.EDIT_PRODUCT -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_AVAILABILITY_FIELD -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_CATEGORY_ERROR -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_CATEGORY_FIELD -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_DESCRIPTION_FIELD -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_NAME_ERROR -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_NAME_FIELD -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_PRICE_ERROR -import com.niyaj.poposroom.features.product.domain.utils.ProductTestTags.PRODUCT_PRICE_FIELD +import com.niyaj.data.utils.PaymentScreenTags +import com.niyaj.data.utils.ProductTestTags.ADD_EDIT_PRODUCT_BUTTON +import com.niyaj.data.utils.ProductTestTags.CREATE_NEW_PRODUCT +import com.niyaj.data.utils.ProductTestTags.EDIT_PRODUCT +import com.niyaj.data.utils.ProductTestTags.PRODUCT_AVAILABILITY_FIELD +import com.niyaj.data.utils.ProductTestTags.PRODUCT_CATEGORY_ERROR +import com.niyaj.data.utils.ProductTestTags.PRODUCT_CATEGORY_FIELD +import com.niyaj.data.utils.ProductTestTags.PRODUCT_DESCRIPTION_FIELD +import com.niyaj.data.utils.ProductTestTags.PRODUCT_NAME_ERROR +import com.niyaj.data.utils.ProductTestTags.PRODUCT_NAME_FIELD +import com.niyaj.data.utils.ProductTestTags.PRODUCT_PRICE_ERROR +import com.niyaj.data.utils.ProductTestTags.PRODUCT_PRICE_FIELD +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.CircularBox +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.utils.Screens +import com.niyaj.ui.utils.UiEvent import com.ramcosta.composedestinations.annotation.Destination -import com.ramcosta.composedestinations.navigation.navigate import com.ramcosta.composedestinations.result.ResultBackNavigator @OptIn(ExperimentalMaterial3Api::class) -@Destination +@Destination( + route = Screens.AddEditProductScreen +) @Composable fun AddEditProductScreen( productId: Int = 0, @@ -99,7 +101,6 @@ fun AddEditProductScreen( LaunchedEffect(key1 = event) { event?.let { data -> when (data) { - is UiEvent.IsLoading -> {} is UiEvent.OnError -> { resultBackNavigator.navigateBack(data.errorMessage) } @@ -146,6 +147,7 @@ fun AddEditProductScreen( .testTag(PaymentScreenTags.ADD_EDIT_PAYMENT_SCREEN) .fillMaxWidth() .padding(SpaceSmall), + verticalArrangement = Arrangement.spacedBy(SpaceSmall) ) { item(PRODUCT_CATEGORY_FIELD) { ExposedDropdownMenuBox( @@ -249,7 +251,7 @@ fun AddEditProductScreen( modifier = Modifier .fillMaxWidth(), onClick = { - navController.navigate(CategoryScreenDestination()) + navController.navigate(Screens.AddEditCategoryScreen) }, text = { Text( @@ -273,8 +275,6 @@ fun AddEditProductScreen( ) } } - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(PRODUCT_NAME_FIELD) { @@ -289,8 +289,6 @@ fun AddEditProductScreen( viewModel.onEvent(AddEditProductEvent.ProductNameChanged(it)) }, ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(PRODUCT_PRICE_FIELD) { @@ -306,8 +304,6 @@ fun AddEditProductScreen( viewModel.onEvent(AddEditProductEvent.ProductPriceChanged(it)) } ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(PRODUCT_DESCRIPTION_FIELD) { @@ -319,8 +315,6 @@ fun AddEditProductScreen( viewModel.onEvent(AddEditProductEvent.ProductDescChanged(it)) } ) - - Spacer(modifier = Modifier.height(SpaceSmall)) } item(PRODUCT_AVAILABILITY_FIELD) { @@ -346,7 +340,6 @@ fun AddEditProductScreen( } Spacer(modifier = Modifier.height(SpaceSmall)) - } } } diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductState.kt b/feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductState.kt similarity index 73% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductState.kt rename to feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductState.kt index 56f500b1..bac53feb 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductState.kt +++ b/feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductState.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.presentation.add_edit +package com.niyaj.product.add_edit data class AddEditProductState( val productName: String = "", diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductViewModel.kt b/feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductViewModel.kt similarity index 88% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductViewModel.kt rename to feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductViewModel.kt index cfb0af75..5d2a2b9b 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/add_edit/AddEditProductViewModel.kt +++ b/feature/product/src/main/java/com/niyaj/product/add_edit/AddEditProductViewModel.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.presentation.add_edit +package com.niyaj.product.add_edit import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -7,16 +7,16 @@ import androidx.compose.runtime.snapshotFlow import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.niyaj.poposroom.features.category.domain.model.Category -import com.niyaj.poposroom.features.common.utils.Dispatcher -import com.niyaj.poposroom.features.common.utils.PoposDispatchers -import com.niyaj.poposroom.features.common.utils.Resource -import com.niyaj.poposroom.features.common.utils.UiEvent -import com.niyaj.poposroom.features.common.utils.safeInt -import com.niyaj.poposroom.features.common.utils.safeString -import com.niyaj.poposroom.features.product.domain.model.Product -import com.niyaj.poposroom.features.product.domain.repository.ProductRepository -import com.niyaj.poposroom.features.product.domain.repository.ProductValidationRepository +import com.niyaj.common.network.Dispatcher +import com.niyaj.common.network.PoposDispatchers +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.safeInt +import com.niyaj.common.utils.safeString +import com.niyaj.data.repository.ProductRepository +import com.niyaj.data.repository.validation.ProductValidationRepository +import com.niyaj.model.Category +import com.niyaj.model.Product +import com.niyaj.ui.utils.UiEvent import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -41,7 +41,7 @@ class AddEditProductViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : ViewModel() { - val productId = savedStateHandle.get("productId") + private val productId = savedStateHandle.get("productId") var state by mutableStateOf(AddEditProductState()) @@ -60,7 +60,7 @@ class AddEditProductViewModel @Inject constructor( @OptIn(ExperimentalCoroutinesApi::class) val categoryError: StateFlow = _selectedCategory .mapLatest { - validationRepository.validateCategoryName(it.categoryId).errorMessage + validationRepository.validateCategoryId(it.categoryId).errorMessage }.stateIn( scope = viewModelScope, started = SharingStarted.WhileSubscribed(5_000), diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/components/ProductSection.kt b/feature/product/src/main/java/com/niyaj/product/components/ProductSection.kt similarity index 97% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/components/ProductSection.kt rename to feature/product/src/main/java/com/niyaj/product/components/ProductSection.kt index 152e0c6f..d2159587 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/components/ProductSection.kt +++ b/feature/product/src/main/java/com/niyaj/product/components/ProductSection.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.presentation.components +package com.niyaj.product.components import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement @@ -34,11 +34,11 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import com.niyaj.poposroom.features.common.ui.theme.IconSizeMedium -import com.niyaj.poposroom.features.common.ui.theme.ProfilePictureSizeSmall -import com.niyaj.poposroom.features.common.ui.theme.SpaceSmall -import com.niyaj.poposroom.features.common.utils.toRupee -import com.niyaj.poposroom.features.product.domain.model.Product +import com.niyaj.common.utils.toRupee +import com.niyaj.designsystem.theme.IconSizeMedium +import com.niyaj.designsystem.theme.ProfilePictureSizeSmall +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.model.Product import kotlin.random.Random diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/settings/ProductSettingScreen.kt b/feature/product/src/main/java/com/niyaj/product/settings/ProductSettingScreen.kt similarity index 70% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/settings/ProductSettingScreen.kt rename to feature/product/src/main/java/com/niyaj/product/settings/ProductSettingScreen.kt index 6954f2ec..148eb21d 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/settings/ProductSettingScreen.kt +++ b/feature/product/src/main/java/com/niyaj/product/settings/ProductSettingScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.presentation.settings +package com.niyaj.product.settings import androidx.compose.runtime.Composable import com.ramcosta.composedestinations.annotation.Destination diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/settings/export_product/ExportProductScreen.kt b/feature/product/src/main/java/com/niyaj/product/settings/export_product/ExportProductScreen.kt similarity index 66% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/settings/export_product/ExportProductScreen.kt rename to feature/product/src/main/java/com/niyaj/product/settings/export_product/ExportProductScreen.kt index e0194393..3c7ef2b3 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/settings/export_product/ExportProductScreen.kt +++ b/feature/product/src/main/java/com/niyaj/product/settings/export_product/ExportProductScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.presentation.settings.export_product +package com.niyaj.product.settings.export_product import androidx.compose.runtime.Composable import com.ramcosta.composedestinations.annotation.Destination diff --git a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/settings/import_product/ImportProductScreen.kt b/feature/product/src/main/java/com/niyaj/product/settings/import_product/ImportProductScreen.kt similarity index 66% rename from app/src/main/java/com/niyaj/poposroom/features/product/presentation/settings/import_product/ImportProductScreen.kt rename to feature/product/src/main/java/com/niyaj/product/settings/import_product/ImportProductScreen.kt index 17948abf..dcd2b561 100644 --- a/app/src/main/java/com/niyaj/poposroom/features/product/presentation/settings/import_product/ImportProductScreen.kt +++ b/feature/product/src/main/java/com/niyaj/product/settings/import_product/ImportProductScreen.kt @@ -1,4 +1,4 @@ -package com.niyaj.poposroom.features.product.presentation.settings.import_product +package com.niyaj.product.settings.import_product import androidx.compose.runtime.Composable import com.ramcosta.composedestinations.annotation.Destination diff --git a/feature/product/src/test/java/com/niyaj/product/ExampleUnitTest.kt b/feature/product/src/test/java/com/niyaj/product/ExampleUnitTest.kt new file mode 100644 index 00000000..92f1982e --- /dev/null +++ b/feature/product/src/test/java/com/niyaj/product/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.product + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/profile/.gitignore b/feature/profile/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/profile/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/profile/build.gradle.kts b/feature/profile/build.gradle.kts new file mode 100644 index 00000000..e73bc569 --- /dev/null +++ b/feature/profile/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.profile" + + ksp { + arg("compose-destinations.moduleName", "profile") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/profile/proguard-rules.pro b/feature/profile/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/profile/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/profile/src/androidTest/java/com/niyaj/profile/ExampleInstrumentedTest.kt b/feature/profile/src/androidTest/java/com/niyaj/profile/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..a34647fe --- /dev/null +++ b/feature/profile/src/androidTest/java/com/niyaj/profile/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.profile + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.profile", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/profile/src/main/AndroidManifest.xml b/feature/profile/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/profile/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/profile/src/main/java/com/niyaj/profile/ProfileScreen.kt b/feature/profile/src/main/java/com/niyaj/profile/ProfileScreen.kt new file mode 100644 index 00000000..9914b517 --- /dev/null +++ b/feature/profile/src/main/java/com/niyaj/profile/ProfileScreen.kt @@ -0,0 +1,102 @@ +package com.niyaj.profile + +import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController +import com.niyaj.data.utils.ProfileTestTags.CREATE_NEW_PROFILE +import com.niyaj.data.utils.ProfileTestTags.PROFILE_NOT_AVAILABLE +import com.niyaj.data.utils.ProfileTestTags.PROFILE_SCREEN +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.profile.destinations.AddEditProfileScreenDestination +import com.niyaj.ui.components.ItemNotAvailable +import com.niyaj.ui.components.LoadingIndicator +import com.niyaj.ui.components.StandardScaffoldNew +import com.niyaj.ui.event.UiState +import com.niyaj.ui.utils.Screens +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph +import com.ramcosta.composedestinations.navigation.navigate +import com.ramcosta.composedestinations.result.NavResult +import com.ramcosta.composedestinations.result.ResultRecipient +import kotlinx.coroutines.launch + +@RootNavGraph(start = true) +@Destination( + route = Screens.ProfileScreen +) +@Composable +fun ProfileScreen( + navController: NavController, + viewModel: ProfileViewModel = hiltViewModel(), + resultRecipient: ResultRecipient, +) { + val snackbarState = remember { SnackbarHostState() } + val lazyListState = rememberLazyListState() + val scope = rememberCoroutineScope() + + val uiState = viewModel.profile.collectAsStateWithLifecycle().value + + resultRecipient.onNavResult { result -> + when (result) { + is NavResult.Canceled -> {} + is NavResult.Value -> { + scope.launch { + snackbarState.showSnackbar(result.value) + } + } + } + } + + StandardScaffoldNew( + navController = navController, + title = PROFILE_SCREEN, + showBottomBar = false, + showBackButton = true, + snackbarHostState = snackbarState, + ) { + Crossfade( + targetState = uiState, + label = "ProfileState" + ) { state -> + when (state) { + is UiState.Empty -> { + ItemNotAvailable( + text = PROFILE_NOT_AVAILABLE, + buttonText = CREATE_NEW_PROFILE, + onClick = { + navController.navigate(AddEditProfileScreenDestination()) + } + ) + } + + is UiState.Loading -> LoadingIndicator() + + is UiState.Success -> { + val profile = state.data + + LazyColumn( + modifier = Modifier + .fillMaxWidth() + .padding(SpaceSmall), + state = lazyListState, + ) { + item { + Text(text = profile.name) + } + } + } + } + } + } +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/niyaj/profile/ProfileViewModel.kt b/feature/profile/src/main/java/com/niyaj/profile/ProfileViewModel.kt new file mode 100644 index 00000000..a619b39c --- /dev/null +++ b/feature/profile/src/main/java/com/niyaj/profile/ProfileViewModel.kt @@ -0,0 +1,32 @@ +package com.niyaj.profile + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.niyaj.data.repository.ProfileRepository +import com.niyaj.ui.event.UiState +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import javax.inject.Inject + +@HiltViewModel +class ProfileViewModel @Inject constructor( + profileRepository: ProfileRepository +) : ViewModel() { + + val profile = profileRepository + .getProfileInfo() + .map { + if (it == null || it.restaurantId == 0) { + UiState.Empty + } else { + UiState.Success(it) + } + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = UiState.Loading + ) +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileEvent.kt b/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileEvent.kt new file mode 100644 index 00000000..ebd0cd57 --- /dev/null +++ b/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileEvent.kt @@ -0,0 +1,27 @@ +package com.niyaj.profile.add_edit + +sealed interface AddEditProfileEvent { + + data class NameChanged(val name: String): AddEditProfileEvent + + data class EmailChanged(val email: String): AddEditProfileEvent + + data class PrimaryPhoneChanged(val primaryPhone: String): AddEditProfileEvent + + data class SecondaryPhoneChanged(val secondaryPhone: String): AddEditProfileEvent + + data class TaglineChanged(val tagline: String): AddEditProfileEvent + + data class DescriptionChanged(val description: String): AddEditProfileEvent + + data class AddressChanged(val address: String): AddEditProfileEvent + + data class LogoChanged(val logo: String): AddEditProfileEvent + + data class PrintLogoChanged(val printLogo: String): AddEditProfileEvent + + data class PaymentQrCodeChanged(val paymentQrCode: String): AddEditProfileEvent + + data class CreateOrUpdateProfileInfo(val restaurantId: Int = 0) : AddEditProfileEvent + +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileScreen.kt b/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileScreen.kt new file mode 100644 index 00000000..cbe95cba --- /dev/null +++ b/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileScreen.kt @@ -0,0 +1,265 @@ +package com.niyaj.profile.add_edit + +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.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.ArrowRightAlt +import androidx.compose.material.icons.filled.Edit +import androidx.compose.material.icons.filled.LocationOn +import androidx.compose.material.icons.filled.Mail +import androidx.compose.material.icons.filled.Notes +import androidx.compose.material.icons.filled.Phone +import androidx.compose.material.icons.filled.PhoneAndroid +import androidx.compose.material.icons.filled.QrCode +import androidx.compose.material.icons.filled.Restaurant +import androidx.compose.material.icons.filled.StarBorder +import androidx.compose.material.icons.outlined.QrCodeScanner +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavController +import com.niyaj.data.utils.ProfileTestTags.ADDRESS_ERROR_FIELD +import com.niyaj.data.utils.ProfileTestTags.ADDRESS_FIELD +import com.niyaj.data.utils.ProfileTestTags.ADD_EDIT_PROFILE_BTN +import com.niyaj.data.utils.ProfileTestTags.ADD_EDIT_PROFILE_SCREEN +import com.niyaj.data.utils.ProfileTestTags.CREATE_NEW_PROFILE +import com.niyaj.data.utils.ProfileTestTags.DESC_ERROR_FIELD +import com.niyaj.data.utils.ProfileTestTags.DESC_FIELD +import com.niyaj.data.utils.ProfileTestTags.EMAIL_ERROR_FIELD +import com.niyaj.data.utils.ProfileTestTags.EMAIL_FIELD +import com.niyaj.data.utils.ProfileTestTags.NAME_ERROR_FIELD +import com.niyaj.data.utils.ProfileTestTags.NAME_FIELD +import com.niyaj.data.utils.ProfileTestTags.P_PHONE_ERROR_FIELD +import com.niyaj.data.utils.ProfileTestTags.P_PHONE_FIELD +import com.niyaj.data.utils.ProfileTestTags.QR_CODE_FIELD +import com.niyaj.data.utils.ProfileTestTags.S_PHONE_ERROR_FIELD +import com.niyaj.data.utils.ProfileTestTags.S_PHONE_FIELD +import com.niyaj.data.utils.ProfileTestTags.TAG_ERROR_FIELD +import com.niyaj.data.utils.ProfileTestTags.TAG_FIELD +import com.niyaj.data.utils.ProfileTestTags.UPDATE_PROFILE +import com.niyaj.designsystem.theme.SpaceSmall +import com.niyaj.designsystem.theme.SpaceSmallMax +import com.niyaj.ui.components.StandardButton +import com.niyaj.ui.components.StandardOutlinedTextField +import com.niyaj.ui.components.StandardScaffoldWithOutDrawer +import com.niyaj.ui.components.TextWithTitle +import com.niyaj.ui.utils.UiEvent +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.result.ResultBackNavigator +import kotlinx.coroutines.flow.collectLatest + +@Destination +@Composable +fun AddEditProfileScreen( + restaurantId: Int = 0, + navController: NavController, + viewModel: AddEditProfileViewModel = hiltViewModel(), + resultBackNavigator: ResultBackNavigator, +) { + val lazyListState = rememberLazyListState() + + val state = viewModel.state + + val title = if (restaurantId == 0) CREATE_NEW_PROFILE else UPDATE_PROFILE + + LaunchedEffect(key1 = true) { + viewModel.eventFlow.collectLatest { + when (it) { + is UiEvent.OnError -> { + resultBackNavigator.navigateBack(it.errorMessage) + } + + is UiEvent.OnSuccess -> { + resultBackNavigator.navigateBack(it.successMessage) + } + } + } + } + + StandardScaffoldWithOutDrawer( + title = title, + showBottomBar = true, + bottomBar = { + StandardButton( + modifier = Modifier + .fillMaxWidth() + .testTag(ADD_EDIT_PROFILE_BTN) + .padding(horizontal = SpaceSmallMax), + enabled = true, + text = title, + icon = if (restaurantId == 0) Icons.Default.Add else Icons.Default.Edit, + onClick = { + viewModel.onEvent(AddEditProfileEvent.CreateOrUpdateProfileInfo(restaurantId)) + } + ) + }, + onBackClick = { + navController.navigateUp() + } + ) { + LazyColumn( + state = lazyListState, + modifier = Modifier + .testTag(ADD_EDIT_PROFILE_SCREEN) + .fillMaxSize() + .padding(SpaceSmall), + ) { + item(NAME_FIELD) { + StandardOutlinedTextField( + value = state.name, + label = NAME_FIELD, + leadingIcon = Icons.Default.Restaurant, + isError = state.nameError != null, + errorText = state.nameError, + errorTextTag = NAME_ERROR_FIELD, + onValueChange = { + viewModel.onEvent(AddEditProfileEvent.NameChanged(it)) + }, + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + + item(EMAIL_FIELD) { + StandardOutlinedTextField( + value = state.email, + label = EMAIL_FIELD, + leadingIcon = Icons.Default.Mail, + isError = state.emailError != null, + errorText = state.emailError, + errorTextTag = EMAIL_ERROR_FIELD, + onValueChange = { + viewModel.onEvent(AddEditProfileEvent.EmailChanged(it)) + }, + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + + item(P_PHONE_FIELD) { + StandardOutlinedTextField( + value = state.primaryPhone, + label = P_PHONE_FIELD, + leadingIcon = Icons.Default.PhoneAndroid, + isError = state.primaryPhoneError != null, + errorText = state.primaryPhoneError, + errorTextTag = P_PHONE_ERROR_FIELD, + onValueChange = { + viewModel.onEvent(AddEditProfileEvent.PrimaryPhoneChanged(it)) + }, + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + + item(S_PHONE_FIELD) { + StandardOutlinedTextField( + value = state.secondaryPhone, + label = S_PHONE_FIELD, + leadingIcon = Icons.Default.Phone, + isError = state.secondaryPhoneError != null, + errorText = state.secondaryPhoneError, + errorTextTag = S_PHONE_ERROR_FIELD, + onValueChange = { + viewModel.onEvent(AddEditProfileEvent.SecondaryPhoneChanged(it)) + }, + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + + item(TAG_FIELD) { + StandardOutlinedTextField( + value = state.tagline, + label = TAG_FIELD, + leadingIcon = Icons.Default.StarBorder, + isError = state.taglineError != null, + errorText = state.taglineError, + errorTextTag = TAG_ERROR_FIELD, + onValueChange = { + viewModel.onEvent(AddEditProfileEvent.TaglineChanged(it)) + }, + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + + item(DESC_FIELD) { + StandardOutlinedTextField( + value = state.description, + label = DESC_FIELD, + leadingIcon = Icons.Default.Notes, + isError = state.descriptionError != null, + errorText = state.descriptionError, + errorTextTag = DESC_ERROR_FIELD, + singleLine = false, + maxLines = 2, + onValueChange = { + viewModel.onEvent(AddEditProfileEvent.DescriptionChanged(it)) + }, + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + + item(ADDRESS_FIELD) { + StandardOutlinedTextField( + value = state.address, + label = ADDRESS_FIELD, + leadingIcon = Icons.Default.LocationOn, + isError = state.addressError != null, + errorText = state.addressError, + errorTextTag = ADDRESS_ERROR_FIELD, + singleLine = false, + maxLines = 2, + onValueChange = { + viewModel.onEvent(AddEditProfileEvent.AddressChanged(it)) + }, + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + + item(QR_CODE_FIELD) { + StandardOutlinedTextField( + value = state.paymentQrCode, + label = QR_CODE_FIELD, + leadingIcon = Icons.Default.QrCode, + trailingIcon = { + IconButton( + onClick = { + + } + ) { + Icon( + imageVector = Icons.Outlined.QrCodeScanner, + contentDescription = "Scan QR Code" + ) + } + }, + onValueChange = { + viewModel.onEvent(AddEditProfileEvent.PaymentQrCodeChanged(it)) + }, + suffix = { + TextWithTitle( + text = "Scan Code", + icon = Icons.Default.ArrowRightAlt + ) + } + ) + + Spacer(modifier = Modifier.height(SpaceSmall)) + } + } + } +} \ No newline at end of file diff --git a/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileState.kt b/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileState.kt new file mode 100644 index 00000000..5068c446 --- /dev/null +++ b/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileState.kt @@ -0,0 +1,31 @@ +package com.niyaj.profile.add_edit + +data class AddEditProfileState( + val name: String = "", + val nameError: String? = null, + + val email: String = "", + val emailError: String? = null, + + val primaryPhone: String = "", + val primaryPhoneError: String? = null, + + val secondaryPhone: String = "", + val secondaryPhoneError: String? = null, + + val tagline: String = "", + val taglineError: String? = null, + + val description: String = "", + val descriptionError: String? = null, + + val address: String = "", + val addressError: String? = null, + + val logo: String = "", + val logoError: String? = null, + + val printLogo: String = "", + + val paymentQrCode: String = "" +) diff --git a/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileViewModel.kt b/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileViewModel.kt new file mode 100644 index 00000000..72304e51 --- /dev/null +++ b/feature/profile/src/main/java/com/niyaj/profile/add_edit/AddEditProfileViewModel.kt @@ -0,0 +1,194 @@ +package com.niyaj.profile.add_edit + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.niyaj.common.result.Resource +import com.niyaj.common.utils.Constants.RESTAURANT_ID +import com.niyaj.data.repository.ProfileRepository +import com.niyaj.data.repository.validation.ProfileValidationRepository +import com.niyaj.model.Profile +import com.niyaj.ui.utils.UiEvent +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class AddEditProfileViewModel @Inject constructor( + private val profileRepository: ProfileRepository, + private val validation: ProfileValidationRepository, + savedStateHandle: SavedStateHandle, +): ViewModel() { + + var state by mutableStateOf(AddEditProfileState()) + + private val _eventFlow = MutableSharedFlow() + val eventFlow = _eventFlow.asSharedFlow() + + init { + savedStateHandle.get("restaurantId")?.let { + getProfileInfo(it) + } + } + + fun onEvent(event: AddEditProfileEvent) { + when(event) { + is AddEditProfileEvent.NameChanged -> { + state = state.copy( + name = event.name, + ) + } + + is AddEditProfileEvent.EmailChanged -> { + state = state.copy( + email = event.email, + ) + } + + is AddEditProfileEvent.PrimaryPhoneChanged -> { + state = state.copy( + primaryPhone = event.primaryPhone, + ) + } + + is AddEditProfileEvent.SecondaryPhoneChanged -> { + state = state.copy( + secondaryPhone = event.secondaryPhone, + ) + } + + is AddEditProfileEvent.TaglineChanged -> { + state = state.copy( + tagline = event.tagline, + ) + } + + is AddEditProfileEvent.DescriptionChanged -> { + state = state.copy( + description = event.description, + ) + } + + is AddEditProfileEvent.AddressChanged -> { + state = state.copy( + address = event.address, + ) + } + + is AddEditProfileEvent.LogoChanged -> { + state = state.copy( + logo = event.logo, + ) + } + + is AddEditProfileEvent.PrintLogoChanged -> { + state = state.copy( + printLogo = event.printLogo, + ) + } + + is AddEditProfileEvent.PaymentQrCodeChanged -> { + state = state.copy( + paymentQrCode = event.paymentQrCode, + ) + } + + is AddEditProfileEvent.CreateOrUpdateProfileInfo -> { + createOrUpdateProfileInfo(event.restaurantId) + } + } + } + + private fun createOrUpdateProfileInfo(restaurantId: Int) { + viewModelScope.launch { + val validateName = validation.validateName(state.name) + val validateEmail = validation.validateEmail(state.email) + val validateDescription = validation.validateDescription(state.description) + val validateTagline = validation.validateTagline(state.tagline) + val validatePrimaryPhone = validation.validatePrimaryPhone(state.primaryPhone) + val validateSecondaryPhone = validation.validateSecondaryPhone(state.secondaryPhone) + val validateAddress = validation.validateAddress(state.address) + val validateLogo = validation.validateLogo(state.logo) + + val hasError = listOf( + validateName, + validateEmail, + validateDescription, + validateAddress, + validateTagline, + validatePrimaryPhone, + validateSecondaryPhone, +// validateLogo, + ).any { !it.successful } + + if (hasError) { + state = state.copy( + nameError = validateName.errorMessage, + emailError = validateEmail.errorMessage, + descriptionError = validateDescription.errorMessage, + taglineError = validateTagline.errorMessage, + primaryPhoneError = validatePrimaryPhone.errorMessage, + secondaryPhoneError = validateSecondaryPhone.errorMessage, + addressError = validateAddress.errorMessage, + logoError = validateLogo.errorMessage + ) + + return@launch + }else { + val newProfile = Profile( + restaurantId = RESTAURANT_ID, + name = state.name, + email = state.email, + primaryPhone = state.primaryPhone, + secondaryPhone = state.secondaryPhone, + tagline = state.tagline, + description = state.description, + address = state.description, + logo = state.description, + printLogo = state.description, + paymentQrCode = state.paymentQrCode, + createdAt = System.currentTimeMillis().toString(), + updatedAt = if (restaurantId != 0) System.currentTimeMillis().toString() else null, + ) + + when(profileRepository.insertOrUpdateProfile(newProfile)) { + is Resource.Error -> { + _eventFlow.emit(UiEvent.OnError("Unable to create or update profile information")) + } + is Resource.Success -> { + _eventFlow.emit(UiEvent.OnSuccess("Profile information created or updated successfully")) + } + } + } + } + } + + private fun getProfileInfo(restaurantId: Int) { + viewModelScope.launch { + profileRepository.getProfileInfo().collectLatest { it -> + it?.let { profile -> + state = state.copy( + name = profile.name, + email = profile.email, + primaryPhone = profile.primaryPhone, + secondaryPhone = profile.secondaryPhone, + tagline = profile.tagline, + description = profile.description, + address = profile.address, + logo = profile.logo, + printLogo = profile.printLogo, + paymentQrCode = profile.paymentQrCode + ) + } + + } + } + } + +} \ No newline at end of file diff --git a/feature/profile/src/test/java/com/niyaj/profile/ExampleUnitTest.kt b/feature/profile/src/test/java/com/niyaj/profile/ExampleUnitTest.kt new file mode 100644 index 00000000..9146e6fa --- /dev/null +++ b/feature/profile/src/test/java/com/niyaj/profile/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.profile + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/feature/settings/.gitignore b/feature/settings/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/feature/settings/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/feature/settings/build.gradle.kts b/feature/settings/build.gradle.kts new file mode 100644 index 00000000..a3b06915 --- /dev/null +++ b/feature/settings/build.gradle.kts @@ -0,0 +1,30 @@ +@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed +plugins { + id("popos.android.feature") + id("popos.android.library.compose") + id("popos.android.library.jacoco") + id("popos.android.hilt") + alias(libs.plugins.ksp) +} + +android { + namespace = "com.niyaj.feature.settings" + + ksp { + arg("compose-destinations.moduleName", "settings") + arg("compose-destinations.mode", "navgraphs") + arg("compose-destinations.useComposableVisibility", "true") + } +} + +dependencies { + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.appcompat) + implementation(libs.androidx.compose.material3) + implementation(libs.accompanist.swiperefresh) + implementation(libs.accompanist.flowlayout) + + //RaamCosta Library + implementation(libs.raamcosta.animation.core) + ksp(libs.raamcosta.ksp) +} \ No newline at end of file diff --git a/feature/settings/proguard-rules.pro b/feature/settings/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/feature/settings/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/settings/src/androidTest/java/com/niyaj/settings/ExampleInstrumentedTest.kt b/feature/settings/src/androidTest/java/com/niyaj/settings/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..ed0ef605 --- /dev/null +++ b/feature/settings/src/androidTest/java/com/niyaj/settings/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.niyaj.settings + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.niyaj.settings", appContext.packageName) + } +} \ No newline at end of file diff --git a/feature/settings/src/main/AndroidManifest.xml b/feature/settings/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/feature/settings/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/settings/src/main/java/com/niyaj/settings/SettingsScreen.kt b/feature/settings/src/main/java/com/niyaj/settings/SettingsScreen.kt new file mode 100644 index 00000000..f5ba3918 --- /dev/null +++ b/feature/settings/src/main/java/com/niyaj/settings/SettingsScreen.kt @@ -0,0 +1,15 @@ +package com.niyaj.settings + +import androidx.compose.runtime.Composable +import androidx.navigation.NavController +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.annotation.RootNavGraph + +@RootNavGraph(start = true) +@Destination +@Composable +fun SettingsScreen( + navController: NavController, +) { +// Todo: implement setting screen +} \ No newline at end of file diff --git a/feature/settings/src/test/java/com/niyaj/settings/ExampleUnitTest.kt b/feature/settings/src/test/java/com/niyaj/settings/ExampleUnitTest.kt new file mode 100644 index 00000000..c6b0f644 --- /dev/null +++ b/feature/settings/src/test/java/com/niyaj/settings/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.niyaj.settings + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index ded09df8..b3975235 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,26 +4,41 @@ # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html + # Specifies the JVM arguments used for the daemon process. -# Enable Configure on demand -org.gradle.configureondemand=true -# Enable simple gradle caching -org.gradle.caching=true # The setting is particularly useful for tweaking memory settings. +# Ensure important default jvmargs aren't overwritten. See https://github.com/gradle/gradle/issues/19750 org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g + # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects - org.gradle.parallel=true +org.gradle.parallel=true + +# Not encouraged by Gradle and can produce weird results. Wait for isolated projects instead. +org.gradle.configureondemand=false + +# Enable caching between builds. +org.gradle.caching=true + +# Enable configuration caching between builds. +org.gradle.unsafe.configuration-cache=true + # AndroidX package structure to make it clearer which packages are bundled with the -# Android operating system, and which are packaged with your app's APK +# Android operating system, and which are packaged with your app"s APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true # Kotlin code style for this project: "official" or "obsolete": kotlin.code.style=official -# Enables namespacing of each library's R class so that its R class includes only the -# resources declared in the library itself and none from the library's dependencies, -# thereby reducing the size of the R class for that library + +# Non-transitive R classes is recommended and is faster/smaller android.nonTransitiveRClass=true -android.defaults.buildfeatures.buildconfig=true -android.nonFinalResIds=false \ No newline at end of file + +# Disable build features that are enabled by default, +# https://developer.android.com/studio/releases/gradle-plugin#buildFeatures +android.defaults.buildfeatures.aidl=false +android.defaults.buildfeatures.renderscript=false +android.defaults.buildfeatures.resvalues=false +android.nonFinalResIds=false +android.defaults.buildfeatures.shaders=false +android.suppressUnsupportedCompileSdk=34 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 28985ef4..aa7fe244 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,262 +1,290 @@ [versions] -androidGradlePlugin = '8.0.1' -kotlin = '1.8.10' -kotlinx-coroutines-version = '1.6.4' -kotlinx-datetime = "0.4.0" -#DI support -dagger-hilt-version = "2.45" -hilt-version = '1.0.0' - -#Database -realm-version = "1.8.0" -room-version = "2.5.1" - -#Code Generation -ksp-version = '1.8.10-1.0.9' - -#AndroidX Versions -androidx-core-version = "1.10.1" -androidx-test-ext-junit = '1.1.5' -androidx-work-version = '2.8.1' -androidx-livedata-version = "1.4.3" -androidx-navigation-compose-version = "2.6.0-beta01" -androidx-test-core-version = '1.5.0' -androidx-test-runner = '1.5.2' -androidx-arch-core-version = '2.2.0' -androidx-startup-version = '1.1.1' -androidx-splash-version = "1.0.1" -androidx-paging-version = "3.1.1" -androidx-paging-compose = "1.0.0-alpha19" - -# App validation and testing -acra-version = '5.9.7' -timber-version = '5.0.1' -leakcanary-version = '2.10' -sentry-version = '3.5.0' -sentry-android-version = '6.17.0' +accompanist = "0.28.0" +accompanistSwipeRefresh = '0.27.0' +acraVersion = '5.9.7' appsweep = 'latest.release' - -#Third party libraries -accompanist-version = '0.30.1' -accompanist-swiperefresh-version = '0.27.0' -revealswipe-version = '1.1.0' -pos-printer-version = '3.2.1' -vanpra-compose-version = '0.9.0' -moshi-version = '1.14.0' +androidDesugarJdkLibs = "2.0.3" +androidGradlePlugin = "8.0.2" +androidxActivity = "1.7.2" +androidxAppCompat = "1.6.1" +androidxArchCore = '2.2.0' +androidxComposeBom = "2023.06.01" +androidxComposeCompiler = "1.5.0" +androidxComposeUi = "1.4.3" +androidxComposeRuntime = "1.4.3" +androidxComposeRuntimeTracing = "1.0.0-alpha03" +androidxComposeMaterial3 = "1.2.0-alpha03" +androidxCore = "1.10.1" +androidxCoreSplashscreen = "1.0.1" +androidxEspresso = "3.5.1" +androidxHiltNavigationCompose = "1.0.0" +androidxLifecycle = "2.6.1" +androidxLivedata = "1.4.3" +androidxMacroBenchmark = "1.2.0-beta01" +androidxBaselineProfile = "1.2.0-alpha13" +androidxBenchmark = "1.1.1" +androidxBenchmarkJunit4 = "1.1.1" +androidxMetrics = "1.0.0-alpha04" +androidxNavigation = "2.6.0" +androidxProfileinstaller = "1.3.1" +androidxStartup = "1.1.1" +androidxTestCore = "1.5.0" +androidxTestExt = "1.1.5" +androidxTestRules = "1.5.0" +androidxTestRunner = "1.5.2" +androidxTracing = "1.1.0" +androidxUiAutomator = "2.2.0" +androidxWindowManager = "1.1.0" +androidxWork = "2.8.1" +coil = "2.4.0" exyte-navigation = '1.0.0' - -#Navigation -raamcosta-version = "1.8.32-beta" - -#Testing -truth-version = '1.1.3' -mockk-version = "1.13.5" -junit = '4.13.2' -espresso-core = '3.5.1' - -# Compose -compose-version = '1.4.0-beta01' -material3-version = '1.2.0-alpha02' -compose-bom = "2023.05.01" -activity-compose = "1.7.1" -lifecycle-version = "2.6.1" -ui-test-junit = '1.4.3' -kotlinComposeExtension = '1.4.1' # As per Kotlin version - -# Profileing & Benchmark Variables -profileinstaller-version = '1.3.1' -uiautomator = '2.2.0' -benchmark-macro = "1.2.0-alpha14" -androidx-baselineprofile = "1.2.0-alpha13" -androidx-benchmark = "1.1.1" -benchmark-junit4 = "1.1.1" +gmsPlugin = "4.3.14" +googleOss = "17.0.1" +googleOssPlugin = "0.10.6" +hilt = "2.47" +hiltExt = "1.0.0" +jacoco = "0.8.7" +junit4 = "4.13.2" +junit = "1.1.5" +kotlin = "1.9.0" +kotlinxCoroutines = "1.6.4" +kotlinxDatetime = "0.4.0" +kotlinxSerializationJson = "1.5.1" +ksp = "1.9.0-1.0.11" +lint = "31.0.2" +leakcanaryVersion = '2.10' +moshiVersion = '1.15.0' +mockkVersion = "1.13.5" +okhttp = "4.10.0" +posPrinterVersion = '3.2.1' +protobuf = "3.23.4" +protobufPlugin = "0.8.19" +playIntegrity = '1.1.0' +playUpdate = "2.1.0" +playScanner = "16.0.0" +playService = "18.2.0" +revealswipeVersion = '1.1.0' +raamcostaVersion = "1.8.32-beta" +realmVersion = "1.8.0" +room = "2.5.2" +secrets = "2.0.1" +sentryVersion = '3.5.0' +sentryAndroidVersion = '6.17.0' +turbine = "0.12.1" +timberVersion = '5.0.1' +truthVersion = '1.1.3' +vanpraComposeVersion = '0.9.0' +zxing = "3.5.1" # App Version namespace = "com.niyaj.poposroom" minSdk = "26" -compileSdk = "33" -targetSdk = "33" +compileSdk = "34" +targetSdk = "34" versionCode = "1" versionName = "1.0.0" +sourceCompatibility = "VERSION_11" +targetCompatibility = "VERSION_11" +jvmTarget = "11" +kotlin1821 = "1.8.21" +androidx-test-ext-junit = "1.1.5" +material = "1.9.0" [libraries] -# Androidx Core -core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-version" } +# ACRA Logger +acra-mail = { group = "ch.acra", name = "acra-mail", version.ref = "acraVersion" } +acra-toast = { group = "ch.acra", name = "acra-toast", version.ref = "acraVersion" } + +# Accompanist library +accompanist-systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist" } +accompanist-testharness = { group = "com.google.accompanist", name = "accompanist-testharness", version.ref = "accompanist" } +accompanist-flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanist" } +accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } +accompanist-swiperefresh = { group = "com.google.accompanist", name = "accompanist-swiperefresh", version.ref = "accompanistSwipeRefresh" } + +# AndroidX Libraries +android-desugarJdkLibs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "androidDesugarJdkLibs" } +# Androidx Activity Compose +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppCompat" } +androidx-benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidxMacroBenchmark" } # Androidx Compose UI -compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } -ui = { group = "androidx.compose.ui", name = "ui" } -ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } -ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } -ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } -ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } -ui-test-junit = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref="ui-test-junit" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } +androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation" } +androidx-compose-foundation-layout = { group = "androidx.compose.foundation", name = "foundation-layout" } # Androidx Compose Material -material-icons = { group = "androidx.compose.material", name = "material-icons-extended" } -material = { group = "androidx.compose.material", name = "material"} -material3 = { group = "androidx.compose.material3", name = "material3", version.ref="material3-version"} -material3-window-size = { group = "androidx.compose.material3", name = "material3-window-size-class", version.ref="material3-version"} +androidx-compose-material-iconsExtended = { group = "androidx.compose.material", name = "material-icons-extended" } +androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "androidxComposeMaterial3" } +androidx-compose-material3-windowSizeClass = { group = "androidx.compose.material3", name = "material3-window-size-class", version.ref = "androidxComposeMaterial3" } + +# Androidx Compose Runtime +androidx-compose-runtime = { group = "androidx.compose.runtime", name = "runtime", version.ref = "androidxComposeRuntime" } +androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "androidxComposeRuntime" } +androidx-compose-runtime-tracing = { group = "androidx.compose.runtime", name = "runtime-tracing", version.ref = "androidxComposeRuntimeTracing" } +androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "androidxComposeUi" } +androidx-compose-ui-test = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "androidxComposeUi" } +androidx-compose-ui-testManifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "androidxComposeUi" } +androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "androidxComposeUi" } +androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview", version.ref = "androidxComposeUi" } +androidx-compose-ui-util = { group = "androidx.compose.ui", name = "ui-util", version.ref = "androidxComposeUi" } +androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics", version.ref = "androidxComposeUi" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidxCore" } -# Androidx Activity Compose -activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" } +# Androidx Startup and Splash Screen +androidx-core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidxCoreSplashscreen" } +androidx-core-startup = { group = "androidx.startup", name = "startup-runtime", version.ref = "androidxStartup" } + +# Androidx Hilt Navigation +androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" } # Androidx Lifecycle -viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle-version" } -viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle-version" } -runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle-version" } -runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle-version" } -viewmodel-savedstate = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-savedstate", version.ref = "lifecycle-version" } -common-java8 = { group = "androidx.lifecycle", name = "lifecycle-common-java8", version.ref = "lifecycle-version" } +androidx-lifecycle-livedata-ktx = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "androidxLifecycle" } +androidx-lifecycle-runtimeCompose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewModelCompose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewmodel-ktx = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "androidxLifecycle" } +androidx-lifecycle-viewmodel-savedstate = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-savedstate", version.ref = "androidxLifecycle" } -# Androidx Run-Time LiveData -runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "androidx-livedata-version" } +androidx-metrics = { group = "androidx.metrics", name = "metrics-performance", version.ref = "androidxMetrics" } # Androidx Navigation -navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidx-navigation-compose-version" } - -# Androidx Startup and Splash Screen -startup = { group = "androidx.startup", name = "startup-runtime", version.ref = "androidx-startup-version" } -splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "androidx-splash-version" } +androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "androidxNavigation" } +androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "androidxNavigation" } + +androidx-profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "androidxProfileinstaller" } +androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidxTestCore" } +androidx-test-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidxEspresso" } +androidx-test-ext = { group = "androidx.test.ext", name = "junit-ktx", version.ref = "androidxTestExt" } +androidx-test-rules = { group = "androidx.test", name = "rules", version.ref = "androidxTestRules" } +androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTestRunner" } +androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "androidxUiAutomator" } +androidx-tracing-ktx = { group = "androidx.tracing", name = "tracing-ktx", version.ref = "androidxTracing" } +androidx-window-manager = { module = "androidx.window:window", version.ref = "androidxWindowManager" } # Androidx Work -work-runtime-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "androidx-work-version" } -work-testing = { group = "androidx.work", name = "work-testing", version.ref = "androidx-work-version" } - -# Accompanist -flowlayout = { group = "com.google.accompanist", name = "accompanist-flowlayout", version.ref = "accompanist-version" } -systemuicontroller = { group = "com.google.accompanist", name = "accompanist-systemuicontroller", version.ref = "accompanist-version" } -permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist-version" } -swiperefresh = { group = "com.google.accompanist", name = "accompanist-swiperefresh", version.ref = "accompanist-swiperefresh-version" } -placeholder-material = { group = "com.google.accompanist", name = "accompanist-placeholder-material", version.ref = "accompanist-version" } -pager = { group = "com.google.accompanist", name = "accompanist-pager", version.ref = "accompanist-version" } -pager-indicators = { group = "com.google.accompanist", name = "accompanist-pager-indicators", version.ref = "accompanist-version" } - -# Coroutines -coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines-version" } -coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines-version" } -# For testing -coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinx-coroutines-version" } +androidx-work-ktx = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "androidxWork" } +androidx-work-testing = { group = "androidx.work", name = "work-testing", version.ref = "androidxWork" } -# Hilt. -hilt-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hilt-version" } -hilt-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hilt-version" } -hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hilt-version" } -# Dagger -hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "dagger-hilt-version" } -hilt-dagger-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "dagger-hilt-version" } -hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "dagger-hilt-version" } +coil-kt = { group = "io.coil-kt", name = "coil", version.ref = "coil" } +coil-kt-compose = { group = "io.coil-kt", name = "coil-compose", version.ref = "coil" } +coil-kt-svg = { group = "io.coil-kt", name = "coil-svg", version.ref = "coil" } -# Timber -timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber-version" } +# Compose Material Dialogs +dialog-core = { group = "io.github.vanpra.compose-material-dialogs", name = "core", version.ref = "vanpraComposeVersion" } +dialog-datetime = { group = "io.github.vanpra.compose-material-dialogs", name = "datetime", version.ref = "vanpraComposeVersion" } -# RevealSwipe -revealswipe = { group = "de.charlex.compose", name = "revealswipe", version.ref = "revealswipe-version" } +# Google OSS Plugins +google-oss-licenses = { group = "com.google.android.gms", name = "play-services-oss-licenses", version.ref = "googleOss" } +google-oss-licenses-plugin = { group = "com.google.android.gms", name = "oss-licenses-plugin", version.ref = "googleOssPlugin" } -# Pos-printer -pos-printer = { group = "com.github.DantSu", name = "ESCPOS-ThermalPrinter-Android", version.ref = "pos-printer-version" } +# Hilt. +hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } +hilt-android-testing = { group = "com.google.dagger", name = "hilt-android-testing", version.ref = "hilt" } +hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } +hilt-ext-compiler = { group = "androidx.hilt", name = "hilt-compiler", version.ref = "hiltExt" } +hilt-ext-work = { group = "androidx.hilt", name = "hilt-work", version.ref = "hiltExt" } -# Realm -realm-library-base = { group = "io.realm.kotlin", name = "library-base", version.ref = "realm-version" } +junit4 = { group = "junit", name = "junit", version.ref = "junit4" } +# Coroutines +kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk8", version.ref = "kotlin" } +kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } +kotlinx-coroutines-guava = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-guava", version.ref = "kotlinxCoroutines" } +# For testing +kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } +kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } +kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } +lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "lint" } # debugImplementation because LeakCanary should only run in debug builds. -leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary-version" } - -# Compose Material Dialogs -dialog-core = { group = "io.github.vanpra.compose-material-dialogs", name = "core", version.ref = "vanpra-compose-version" } -dialog-datetime = { group = "io.github.vanpra.compose-material-dialogs", name = "datetime", version.ref = "vanpra-compose-version" } - -# RamCosta Library -raamcosta-core = { group = "io.github.raamcosta.compose-destinations", name = "core", version.ref = "raamcosta-version" } -raamcosta-animation-core = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "raamcosta-version" } -raamcosta-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "raamcosta-version" } +leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanaryVersion" } # Moshi -moshi-kotlin-codegen = { group = "com.squareup.moshi", name = "moshi-kotlin-codegen", version.ref = "moshi-version" } -moshi = { group = "com.squareup.moshi", name = "moshi", version.ref = "moshi-version" } +moshi-kotlin-codegen = { group = "com.squareup.moshi", name = "moshi-kotlin-codegen", version.ref = "moshiVersion" } +moshi = { group = "com.squareup.moshi", name = "moshi", version.ref = "moshiVersion" } -# ProfileInstaller -profileinstaller = { group = "androidx.profileinstaller", name = "profileinstaller", version.ref = "profileinstaller-version" } +# Mockk +mockk-android = { group = "io.mockk", name = "mockk-android", version.ref = "mockkVersion" } +mockk = { group = "io.mockk", name = "mockk", version.ref = "mockkVersion" } -# Junit -junit = { group = "junit", name = "junit", version.ref = "junit" } +# Exyte Animated Bottom Navigation +navigation-bar = { group = "com.exyte", name = "animated-navigation-bar", version.ref = "exyte-navigation"} -# Androidx Test -androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } -espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" } +# Pos-printer +pos-printer = { group = "com.github.DantSu", name = "ESCPOS-ThermalPrinter-Android", version.ref = "posPrinterVersion" } -# Androidx Test Core -androidx-test-core = { group = "androidx.test", name = "core", version.ref = "androidx-test-core-version" } -androidx-test-core-ktx = { group = "androidx.test", name = "core-ktx", version.ref = "androidx-test-core-version" } -androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidx-test-runner" } +#Google Play +play-app-update = { group = "com.google.android.play", name = "app-update", version.ref="playUpdate"} +play-app-update-ktx = { group = "com.google.android.play", name = "app-update-ktx", version.ref="playUpdate"} +play-gms-scanner = { group = "com.google.android.gms", name = "play-services-code-scanner", version.ref = "playScanner"} +play-integrity = { group = "com.google.android.play", name = "integrity", version.ref = "playIntegrity"} +play-service = {group = "com.google.android.gms", name = "play-services-base", version.ref = "playService"} -# Androidx Arch Test -androidx-arch-core-testing = { group = "androidx.arch.core", name = "core-testing", version.ref = "androidx-arch-core-version" } +#Protobuf +protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } +protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } -# Truth -truth = { group = "com.google.truth", name = "truth", version.ref = "truth-version" } +# Room Database +room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" } +room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } +room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } +room-paging = { group = "androidx.room", name = "room-paging", version.ref = "room" } -# Mockk -mockk-android = { group = "io.mockk", name = "mockk-android", version.ref = "mockk-version" } -mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk-version" } +# RevealSwipe +revealswipe = { group = "de.charlex.compose", name = "revealswipe", version.ref = "revealswipeVersion" } +# Realm +realm-library-base = { group = "io.realm.kotlin", name = "library-base", version.ref = "realmVersion" } -# ACRA Logger -acra-mail = { group = "ch.acra", name = "acra-mail", version.ref = "acra-version" } -acra-toast = { group = "ch.acra", name = "acra-toast", version.ref = "acra-version" } -acra-notification = { group = "ch.acra", name = "acra-notification", version.ref = "acra-version" } -acra-limiter = { group = "ch.acra", name = "acra-limiter", version.ref = "acra-version" } -acra-advanced-scheduler = { group = "ch.acra", name = "acra-advanced-scheduler", version.ref = "acra-version" } +# RamCosta Library +raamcosta-animation-core = { group = "io.github.raamcosta.compose-destinations", name = "animations-core", version.ref = "raamcostaVersion" } +raamcosta-ksp = { group = "io.github.raamcosta.compose-destinations", name = "ksp", version.ref = "raamcostaVersion" } # Sentry -sentry-android = { group = "io.sentry", name = "sentry-android", version.ref = "sentry-android-version" } -sentry-compose-android = { group = "io.sentry", name = "sentry-compose-android", version.ref = "sentry-android-version" } +sentry-android = { group = "io.sentry", name = "sentry-android", version.ref = "sentryAndroidVersion" } +sentry-compose-android = { group = "io.sentry", name = "sentry-compose-android", version.ref = "sentryAndroidVersion" } -# Dependencies of the included build-logic -android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } -kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } -kotlin-serialization-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-serialization", version.ref = "kotlin" } -hilt-gradlePlugin = { group = "com.google.dagger", name = "hilt-android-gradle-plugin", version.ref = "dagger-hilt-version" } -realm-gradlePlugin = { group = "io.realm.kotlin", name = "gradle-plugin", version.ref = "realm-version" } +turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" } -# Kotlin Bom -kotlin-bom = { group = "org.jetbrains.kotlin", name = "kotlin-bom", version.ref = "kotlin" } +# Timber +timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timberVersion" } -# Benchmarking -uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" } -benchmark-macro = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmark-macro" } -benchmark-junit4 = { group = "androidx.benchmark", name = "benchmark-junit4", version.ref = "benchmark-junit4" } +# Truth +truth = { group = "com.google.truth", name = "truth", version.ref = "truthVersion" } -#Room -room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room-version"} -room-complier = { group = "androidx.room", name = "room-compiler", version.ref = "room-version"} -room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room-version"} -room-testing = { group = "androidx.room", name = "room-testing", version.ref = "room-version"} -room-paging = { group = "androidx.room", name = "room-paging", version.ref = "room-version"} +# zxing core & zxing-android-embedded +zxing-core = { group = "com.google.zxing", name = "core", version.ref = "zxing"} -#Paging 3 -paging-runtime = { group = "androidx.paging", name = "paging-runtime", version.ref = "androidx-paging-version"} -paging-compose = { group = "androidx.paging", name = "paging-compose", version.ref = "androidx-paging-compose"} -#KotlinX DateTime -kotlinx-date = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinx-datetime"} +# Dependencies of the included build-logic +android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } +kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } +ksp-gradlePlugin = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } +hilt-gradlePlugin = { group = "com.google.dagger", name = "hilt-android-gradle-plugin", version.ref = "hilt" } +realm-gradlePlugin = { group = "io.realm.kotlin", name = "gradle-plugin", version.ref = "realmVersion" } +junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } -# Exyte Animated Bottom Navigation -navigation-bar = { group = "com.exyte", name = "animated-navigation-bar", version.ref = "exyte-navigation"} [plugins] android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } android-test = { id = "com.android.test", version.ref = "androidGradlePlugin" } -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } -kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } -kotlin-parcelize = { id = "kotlin-parcelize", version.ref = "kotlin" } -hilt = { id = "com.google.dagger.hilt.android", version.ref = "dagger-hilt-version" } -realm = { id = "io.realm.kotlin", version.ref = "realm-version" } +androidx-baselineprofile = { id = "androidx.baselineprofile", version.ref = "androidxBaselineProfile" } +androidx-benchmark = { id = "androidx.benchmark", version.ref = "androidxBenchmark" } appsweep = { id = "com.guardsquare.appsweep", version.ref = "appsweep" } -sentry = { id = "io.sentry.android.gradle", version.ref = "sentry-version" } -ksp = { id = "com.google.devtools.ksp", version.ref = "ksp-version" } +gms = { id = "com.google.gms.google-services", version.ref = "gmsPlugin" } +hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-kapt = { id = "kotlin-kapt", version.ref = "kotlin" } -androidx-baselineprofile = { id = "androidx.baselineprofile", version.ref = "androidx-baselineprofile" } -androidx-benchmark = { id = "androidx.benchmark", version.ref = "androidx-benchmark" } \ No newline at end of file +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } +protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } +realm = { id = "io.realm.kotlin", version.ref = "realmVersion" } +sentry = { id = "io.sentry.android.gradle", version.ref = "sentryVersion" } +secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } +kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin1821" } + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 4d326611..388e2fff 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,5 @@ pluginManagement { + includeBuild("build-logic") repositories { google() mavenCentral() @@ -16,4 +17,32 @@ dependencyResolutionManagement { rootProject.name = "PoposRoom" include(":app") - \ No newline at end of file +include(":benchmark") +include(":core:data") +include(":core:domain") +include(":core:model") +include(":core:database") +include(":core:ui") +include(":core:common") +include(":core:designsystem") +include(":core:testing") +include(":feature:addonitem") +include(":feature:address") +include(":feature:cart") +include(":feature:cartorder") +include(":feature:category") +include(":feature:charges") +include(":feature:customer") +include(":feature:employee") +include(":feature:employee_payment") +include(":feature:employee_absent") +include(":feature:expenses") +include(":feature:home") +include(":feature:account") +include(":feature:order") +include(":feature:product") +include(":feature:print_order") +include(":feature:profile") +include(":feature:cart_selected") +include(":feature:settings") +include(":feature:printer_info")