diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml
index 8a4a7038b..5789de63f 100644
--- a/androidApp/src/main/AndroidManifest.xml
+++ b/androidApp/src/main/AndroidManifest.xml
@@ -19,7 +19,7 @@
android:name=".MifosSelfServiceApp"
android:allowBackup="true"
android:icon="@mipmap/core_common_mifos_icon"
- android:label="@string/app_name"
+ android:label="@string/feature_about_app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
@@ -36,7 +36,7 @@
()
arrayList.add(
CheckboxStatus(
- context?.getString(R.string.deposit),
+ context?.getString(R.string.feature_account_deposit),
ContextCompat
.getColor(context!!, R.color.deposit_green),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.dividend_payout),
+ context.getString(R.string.feature_account_dividend_payout),
ContextCompat
.getColor(context, R.color.red_light),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.withdrawal),
+ context.getString(R.string.feature_account_withdrawal),
ContextCompat
.getColor(context, R.color.red_light),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.interest_posting),
+ context.getString(R.string.feature_account_interest_posting),
ContextCompat.getColor(context, R.color.green_light),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.fee_deduction),
+ context.getString(R.string.feature_account_fee_deduction),
ContextCompat
.getColor(context, R.color.red_light),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.withdrawal_transfer),
+ context.getString(R.string.feature_account_withdrawal_transfer),
ContextCompat.getColor(context, R.color.red_light),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.rejected_transfer),
+ context.getString(R.string.feature_account_rejected_transfer),
ContextCompat.getColor(context, R.color.green_light),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.overdraft_fee),
+ context.getString(R.string.feature_account_overdraft_fee),
ContextCompat
.getColor(context, R.color.red_light),
),
diff --git a/androidApp/src/main/java/org/mifos/mobile/utils/fcm/MifosFirebaseMessagingService.kt b/androidApp/src/main/java/org/mifos/mobile/utils/fcm/MifosFirebaseMessagingService.kt
index 3f2ef6ad4..39523e0cc 100644
--- a/androidApp/src/main/java/org/mifos/mobile/utils/fcm/MifosFirebaseMessagingService.kt
+++ b/androidApp/src/main/java/org/mifos/mobile/utils/fcm/MifosFirebaseMessagingService.kt
@@ -62,7 +62,7 @@ class MifosFirebaseMessagingService : FirebaseMessagingService() {
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.core_common_mifos_icon)
- .setContentTitle(getString(R.string.app_name))
+ .setContentTitle(getString(R.string.feature_about_app_name))
.setContentText(message)
.setAutoCancel(true)
.setSound(defaultSoundUri)
diff --git a/androidApp/src/main/res/drawable/ic_wrapped_lock_black_24dp.xml b/androidApp/src/main/res/drawable/ic_wrapped_lock_black_24dp.xml
index d2a2d1913..f0b49c99e 100644
--- a/androidApp/src/main/res/drawable/ic_wrapped_lock_black_24dp.xml
+++ b/androidApp/src/main/res/drawable/ic_wrapped_lock_black_24dp.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/androidApp/src/main/res/drawable/ic_wrapped_person_black_24dp.xml b/androidApp/src/main/res/drawable/ic_wrapped_person_black_24dp.xml
index fde1e3a50..0b1822d0a 100644
--- a/androidApp/src/main/res/drawable/ic_wrapped_person_black_24dp.xml
+++ b/androidApp/src/main/res/drawable/ic_wrapped_person_black_24dp.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/androidApp/src/main/res/layout/activity_login.xml b/androidApp/src/main/res/layout/activity_login.xml
index 11d86841a..6d2c55418 100644
--- a/androidApp/src/main/res/layout/activity_login.xml
+++ b/androidApp/src/main/res/layout/activity_login.xml
@@ -19,13 +19,13 @@
android:layout_height="@dimen/Mifos.DesignSystem.Size.LogoImageSize"
android:layout_gravity="center_horizontal"
android:layout_marginVertical="@dimen/Mifos.DesignSystem.Size.LogoImageVerticalMargin"
- app:srcCompat="@drawable/mifos_logo" />
+ app:srcCompat="@drawable/feature_auth_mifos_logo" />
+ app:startIconDrawable="@drawable/feature_auth_ic_person">
+ app:startIconDrawable="@drawable/feature_auth_lock">
+ tools:src="@drawable/feature_auth_ic_person" />
+ app:srcCompat="@drawable/feature_auth_ic_person" />
+ tools:src="@drawable/feature_auth_ic_person" />
+ android:text="@string/feature_account_deposit" />
+ android:text="@string/feature_account_cancel" />
diff --git a/androidApp/src/main/res/layout/fragment_third_party_transfer.xml b/androidApp/src/main/res/layout/fragment_third_party_transfer.xml
index 6e918acba..411f92920 100644
--- a/androidApp/src/main/res/layout/fragment_third_party_transfer.xml
+++ b/androidApp/src/main/res/layout/fragment_third_party_transfer.xml
@@ -311,7 +311,7 @@
android:layout_marginTop="@dimen/margin_10dp"
android:layout_marginBottom="@dimen/margin_16dp"
android:layout_weight="0.5"
- android:text="@string/cancel"
+ android:text="@string/feature_account_cancel"
android:textColor="?attr/colorPrimary"
app:backgroundTint="@color/white" />
diff --git a/androidApp/src/main/res/layout/fragment_transfer_process.xml b/androidApp/src/main/res/layout/fragment_transfer_process.xml
index 405c776e1..912e5f323 100644
--- a/androidApp/src/main/res/layout/fragment_transfer_process.xml
+++ b/androidApp/src/main/res/layout/fragment_transfer_process.xml
@@ -143,7 +143,7 @@
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/cancel"
+ android:text="@string/feature_account_cancel"
android:textColor="?attr/colorPrimary" />
+ tools:srcCompat="@drawable/feature_auth_ic_person" />
+ app:startIconDrawable="@drawable/feature_auth_lock">
+ android:text="@string/feature_account_approved" />
diff --git a/androidApp/src/main/res/menu/menu_nav_drawer.xml b/androidApp/src/main/res/menu/menu_nav_drawer.xml
index 59f118c2a..656120b96 100644
--- a/androidApp/src/main/res/menu/menu_nav_drawer.xml
+++ b/androidApp/src/main/res/menu/menu_nav_drawer.xml
@@ -49,7 +49,7 @@
+ android:title="@string/feature_about_about_us" />
+ android:title="@string/feature_account_share" />
-
- ميفوس موبايل
+ ميفوس موبايل
تسجيل الدخول
%1$s أهلا بك
غير متصل بالإنترنت
@@ -16,9 +16,9 @@
التحويلات الاخيرة
شحنة
استطلاع
- معلومات عنا
+ معلومات عنا
حفظ تفاصيل الحساب
- حساب التوفير
+ حساب التوفير
خطأ في تحميل تفاصيل حسابات القروض
معدل الفائدة الاسمي
رقم حساب
@@ -31,11 +31,11 @@
نص صغير
حسابات العملاء
دفقة
- إنقاذ
- قرض
- حساب القرض
- شارك
- حصة الحساب
+ إنقاذ
+ قرض
+ حساب القرض
+ شارك
+ حصة الحساب
شيء العميل
العمل
صورة الحالة
@@ -50,8 +50,8 @@
تعليق
تاريخ الصرف المتوقع
تاريخ التقديم
- قدمت
- إنفاق
+ قدمت
+ إنفاق
تاريخ التحويل
التقدم بطلب للحصول على قرض
تحديث القرض
@@ -67,23 +67,23 @@
استراتيجية السداد
دفع إلى
ادفع من
- إلغاء
+ إلغاء
إعادة النظر
حول إلى
تحويل من
جعل النقل
- الوديعة
+ الوديعة
أدخل المبلغ
ملاحظة إلزامية
- وافق
+ وافق
قيد الانتظار
دفع
توازن
مرفوض
انتظار
- زائدة
- في المتأخرات
- حدد كل ما تريد تطبيقه
+ زائدة
+ في المتأخرات
+ حدد كل ما تريد تطبيقه
تصفية حسابات التوفير
تصفية حسابات القروض
تصفية حصة الحسابات
@@ -99,12 +99,12 @@
نقل من الادخار
رسوم القرض
رسوم الادخار
- نشيط
- مغلق
- سحب
+ نشيط
+ مغلق
+ سحب
تحتاج الى موافقة
- قيد الانتظار
- نضجت
+ قيد الانتظار
+ نضجت
انشئ حساب
الاسم الاول
الكنية
@@ -135,9 +135,9 @@
يجب أن يكون الحساب نشطًا لتنفيذ الوديعة
يجب أن يكون الحساب نشطًا لتنفيذ نقل
- لا يوجد حساب SavingsAccount مرتبط بك
- لا يوجد LoanAccount مرتبط بك
- لا يوجد ShareAccount مرتبط بك
+ لا يوجد حساب SavingsAccount مرتبط بك
+ لا يوجد LoanAccount مرتبط بك
+ لا يوجد ShareAccount مرتبط بك
لا توجد معاملات مرتبطة بك
لا يوجد جدول سداد مرتبط بك
لا مزيد من المعاملات المتاحة
@@ -230,8 +230,8 @@
جدول الدفع
المعاملات
نقل
- في انتظار الموافقة
- في انتظار الصرف
+ في انتظار الموافقة
+ في انتظار الصرف
مغلقة بسبب بعض الالتزامات
القرض مغلق
تاريخ الاستحقاق:
@@ -255,7 +255,7 @@
4 أسابيع
3 اشهر
6 اشهر
- منقي
+ منقي
تاريخ البدء
تاريخ الانتهاء
مصفى
@@ -368,7 +368,7 @@
%1$s ،مرحبا
%1$.2f
%2$s %1$.2f
- %2$s %1$s
+ %2$s %1$s
%2$.2f %1$s
%2$d :%1$s
مبلغ غير صحيح
@@ -392,7 +392,7 @@
اترك بريدًا إلكترونيًا
البحث عن المواقع
لا انسحابات
- مسح مرشحات
+ مسح مرشحات
إدارة الحسابات
أكثر من
الإعدادات
@@ -429,9 +429,9 @@
تأكيد كلمة المرور
كلمة السر غير متطابقة.
%1$s الإصدار
- ©2016-%1$s Mifos Initiative.
+ ©2016-%1$s Mifos Initiative.
كل الحقوق محفوظة.
- تراخيص
+ تراخيص
يتعذر إكمال النقل ، يرجى المحاولة مرة أخرى في وقت لاحق
ارجوك انتظر…
رسالة
diff --git a/androidApp/src/main/res/values-bn/strings.xml b/androidApp/src/main/res/values-bn/strings.xml
index 0983e9c61..267a97235 100644
--- a/androidApp/src/main/res/values-bn/strings.xml
+++ b/androidApp/src/main/res/values-bn/strings.xml
@@ -14,9 +14,9 @@
সাম্প্রতিক লেনদেন
ফি
প্রশ্নাবলী
- আমাদের সম্পর্কে
+ আমাদের সম্পর্কে
অ্যাকাউন্ট বিবরণ সংরক্ষণ করা হচ্ছে
- সঞ্চয় অ্যাকাউন্ট
+ সঞ্চয় অ্যাকাউন্ট
ক্রেডিট অ্যাকাউন্ট তথ্য লোড করার সময় ত্রুটি
নামমাত্র সুদের হার
অ্যাকাউন্ট নম্বর
@@ -29,11 +29,11 @@
ছোট টেক্সট
গ্রাহক অ্যাকাউন্ট
স্বাগতম পর্দা
- সঞ্চয়
- ঋণ
- ক্রেডিট অ্যাকাউন্ট
- শেয়ার
- আপনার অ্যাকাউন্ট শেয়ার করুন
+ সঞ্চয়
+ ঋণ
+ ক্রেডিট অ্যাকাউন্ট
+ শেয়ার
+ আপনার অ্যাকাউন্ট শেয়ার করুন
একটি গ্রাহক চয়ন করুন
কাজ
রাষ্ট্রের ছবি
@@ -48,8 +48,8 @@
সতর্কতা
প্রত্যাশিত বেতন তারিখ
জমা দেওয়ার জন্য নির্দিষ্ট সময়সীমা
- আপলোড
- ব্যয়
+ আপলোড
+ ব্যয়
স্থানান্তর তারিখ
একটি ঋণ জন্য আবেদন করুন
ঋণ আপডেট করুন
@@ -65,23 +65,23 @@
ঋণ পরিশোধের কৌশল
পেমেন্ট
থেকে পেমেন্ট
- বাতিল
+ বাতিল
পূর্বরূপ
স্থানান্তর
থেকে স্থানান্তর
একটি স্থানান্তর করা
- আমানত
+ আমানত
পরিমাণ লিখুন
নোট বাধ্যতামূলক
- অনুমোদিত
+ অনুমোদিত
প্রত্যাশা
অর্থ প্রদান
ভারসাম্য
প্রত্যাখ্যাত
প্রতীক্ষা
- অতিরিক্ত
- বকেয়া
- আপনি আবেদন করতে চান সবকিছু চয়ন করুন
+ অতিরিক্ত
+ বকেয়া
+ আপনি আবেদন করতে চান সবকিছু চয়ন করুন
ফিল্টার সঞ্চয় অ্যাকাউন্ট
ফিল্টার ক্রেডিট অ্যাকাউন্ট
ফিল্টার অ্যাকাউন্ট শেয়ারিং
@@ -97,12 +97,12 @@
সঞ্চয় থেকে স্থানান্তর
ঋণের জন্য ফি
জমা
- সক্রিয়
- বন্ধ
- অপসারিত
+ সক্রিয়
+ বন্ধ
+ অপসারিত
অনুমোদন প্রয়োজন
- প্রত্যাশা
- পরিণত
+ প্রত্যাশা
+ পরিণত
একটি অ্যাকাউন্ট তৈরি করুন
নাম
নাম
@@ -133,9 +133,9 @@
অ্যাকাউন্ট একটি আমানত করতে সক্রিয় করা উচিত
অ্যাকাউন্ট একটি স্থানান্তর করতে সক্রিয় করা উচিত
- আপনার সাথে সংযুক্ত কোন সঞ্চয় অ্যাকাউন্ট আছে
- আপনার সাথে যুক্ত কোন ঋণ অ্যাকাউন্ট আছে
- আপনার সাথে যুক্ত কোন ভাগ অ্যাকাউন্ট আছে
+ আপনার সাথে সংযুক্ত কোন সঞ্চয় অ্যাকাউন্ট আছে
+ আপনার সাথে যুক্ত কোন ঋণ অ্যাকাউন্ট আছে
+ আপনার সাথে যুক্ত কোন ভাগ অ্যাকাউন্ট আছে
আপনার সাথে কোন লেনদেন আছে
আপনার সাথে যুক্ত কোন পরিশোধের সময়সূচী নেই
কোন লেনদেন উপলব্ধ নেই
@@ -225,8 +225,8 @@
পরিশোধের সময়সূচী
লেনদেন
হস্তান্তর
- অগ্রগতি অনুমোদন
- পেমেন্ট জন্য অপেক্ষা করছে
+ অগ্রগতি অনুমোদন
+ পেমেন্ট জন্য অপেক্ষা করছে
কিছু বাধ্যবাধকতা কারণে বন্ধ
ঋণ বন্ধ
তারিখ অনুসারে:
@@ -247,7 +247,7 @@
৪ সপ্তাহ
৩ মাস
৬ মাস
- ফিল্টার
+ ফিল্টার
শুরু তারিখ
সমাপ্তির তারিখ
ফিল্টার
@@ -368,7 +368,7 @@
একটি ইমেইল বার্তা ছেড়ে দিন
অবস্থান খুঁজুন
কোন পেমেন্ট
- ফিল্টার সাফ করুন
+ ফিল্টার সাফ করুন
অ্যাকাউন্ট পরিচালনা করুন
অধিক
সেটিংস
@@ -406,7 +406,7 @@
পাসওয়ার্ড মেলে না।
সংস্করণ %1$s
সমস্ত অধিকার সংরক্ষিত।
- লাইসেন্সকরণ
+ লাইসেন্সকরণ
একটি স্থানান্তর করতে পারবেন না, পরে আবার চেষ্টা করুন
অনুগ্রহ করে অপেক্ষা করুন …
বার্তা
diff --git a/androidApp/src/main/res/values-es/strings.xml b/androidApp/src/main/res/values-es/strings.xml
index 0a0da8e94..708639a7e 100644
--- a/androidApp/src/main/res/values-es/strings.xml
+++ b/androidApp/src/main/res/values-es/strings.xml
@@ -14,9 +14,9 @@
Transacciones recientes
Honorarios
Cuestionario
- Sobre nosotros
+ Sobre nosotros
Guardar detalles de la cuenta
- Cuenta de ahorros
+ Cuenta de ahorros
Error al cargar la información de la cuenta de crédito
Tasas de interés nominales
Numero de cuenta
@@ -29,11 +29,11 @@
Texto pequeño
Cuentas de clientes
Pantalla de bienvenida
- Ahorro
- Préstamo
- Cuenta de credito
- Cuota
- Comparte tu cuenta
+ Ahorro
+ Préstamo
+ Cuenta de credito
+ Cuota
+ Comparte tu cuenta
Elige un cliente
Laboral
Imagen del estado
@@ -48,8 +48,8 @@
Precaución
Fecha de pago esperada
Plazo de presentación.
- Subidas
- Gasto
+ Subidas
+ Gasto
Fecha de transferencia
Solicitar un préstamo
Actualizar el prestamo
@@ -65,23 +65,23 @@
Estrategia de pago
Pago a
Pago desde
- Cancelar
+ Cancelar
Preestreno
Transferir a
Transferencia de
Haciendo una transferencia
- Depósito
+ Depósito
Introduce la cantidad
Nota es obligatoria
- Aprobado
+ Aprobado
En anticipación
Pagado
Equilibrio
Rechazado
Espera
- Pagado en exceso
- En mora
- Elige todo lo que quieras aplicar.
+ Pagado en exceso
+ En mora
+ Elige todo lo que quieras aplicar.
Filtrar cuentas de ahorro.
Filtrar cuentas de crédito
Filtrar el intercambio de cuentas
@@ -97,12 +97,12 @@
Transferencia de ahorros
Cargos por préstamos
Ahorros
- Activo
- Cerrado
- Retirado
+ Activo
+ Cerrado
+ Retirado
Requiere aprobación
- En anticipación
- Maduro
+ En anticipación
+ Maduro
Crear una cuenta
Nombre
Nombre
@@ -133,9 +133,9 @@
La cuenta debe estar activa para hacer un depósito.
La cuenta debe estar activa para realizar una transferencia.
- No hay cuentas de ahorro vinculadas a usted.
- No hay cuentas de préstamo asociadas con usted
- No hay cuentas compartidas asociadas con usted
+ No hay cuentas de ahorro vinculadas a usted.
+ No hay cuentas de préstamo asociadas con usted
+ No hay cuentas compartidas asociadas con usted
No hay transacciones relacionadas con usted.
No hay un calendario de pago asociado con usted
No hay transacciones disponibles
@@ -225,8 +225,8 @@
Calendario de reembolso
Transacciones
Transferencia
- Aprobación en curso
- Esperando el pago
+ Aprobación en curso
+ Esperando el pago
Cerrado debido a algunas obligaciones.
Préstamo cerrado
Por fecha:
@@ -247,7 +247,7 @@
4 semanas
Tres meses
6 meses
- Filtro
+ Filtro
Fecha de inicio
Fecha de finalización
Filtrado
@@ -370,7 +370,7 @@
Deja un mensaje de correo electrónico
Encontrar ubicaciones
Sin pagos
- Borrar filtros
+ Borrar filtros
Administrar cuentas
Más
Ajustes
@@ -408,7 +408,7 @@
La contraseña no coincide.
Versión%1$s
Todos los derechos reservados.
- La concesión de licencias
+ La concesión de licencias
No se puede hacer una transferencia, por favor intente de nuevo más tarde
Por favor espere₀
Mensaje
diff --git a/androidApp/src/main/res/values-fa-rAF/strings.xml b/androidApp/src/main/res/values-fa-rAF/strings.xml
index 0dda345c3..857819144 100644
--- a/androidApp/src/main/res/values-fa-rAF/strings.xml
+++ b/androidApp/src/main/res/values-fa-rAF/strings.xml
@@ -1,7 +1,7 @@
- Mifos Mobile
- درباره ما
+ Mifos Mobile
+ درباره ما
"درباره ما در ساخت و ساز "
موجودی حساب
شناسه حساب
@@ -14,7 +14,7 @@
حساب ها
نمای کلی حساب
تاریخ فعال سازی
- فعال
+ فعال
فعال
اضافه کردن
ذینفع اضافه کنید
@@ -34,8 +34,8 @@
درخواست دادن
درخواست وام دهید
درخواست حساب پس انداز
- در انتظار تایید
- تایید شده
+ در انتظار تایید
+ تایید شده
مثال
تأیید اعتبار
تعادل
@@ -48,7 +48,7 @@
نام ذینفع
ذینفع با موفقیت به روز شد
تماس بگیر
- لغو
+ لغو
آیا مطمئن هستید که می خواهید انتقال را لغو کنید؟
گذرواژه حساب خود را تغییر دهید
تغییر کد عبور برنامه
@@ -60,7 +60,7 @@
زبان خود را انتخاب کنید
گزینه را انتخاب کنید
نوع انتقال را انتخاب کنید
- فیلترها را پاک کنید
+ فیلترها را پاک کنید
حساب های مشتری
هزینه مشتری
طبقه بندی مشتری
@@ -70,7 +70,7 @@
مشتری را انتخاب کنید
بستن
کشو را ببندید
- بسته شد
+ بسته شد
به دلیل برخی تعهدات بسته شد
رمز عبور را تأیید کنید
ادامه هید
@@ -82,7 +82,7 @@
ذینفع را حذف کنید
مطمئن هستید که می خواهید این مزایا را حذف کنید
ضمانت را حذف کنید
- سپرده
+ سپرده
جزئیات
تنظیمات برنامه
بازگشت
@@ -99,17 +99,17 @@
بدون اجازه ذخیره نمی توانید کد QR را برای اضافه کردن ذینفع بارگذاری کنید. آیا مطمئن هستید که می خواهید این مجوز را انکار کنید؟
شما مجوز نوشتن در انبار را رد کرده اید ، بدون این مجوز قادر نخواهید بود با استفاده از QR Code ذینفع اضافه کنید. لطفاً آن را در تنظیمات فعال کنید
اجازه رد شد
- پرداخت
+ پرداخت
تاریخ پرداخت
- پرداخت سود سهام
+ پرداخت سود سهام
سر رسید
سر رسید:
ویرایش
پست الکترونیک
- هیچ حساب قرضه مربوط به شما وجود ندارد
+ هیچ حساب قرضه مربوط به شما وجود ندارد
هیچ برنامه ای بازپرداخت وجود ندارد
- هیچ حساب پس انداز مرتبط با شما وجود ندارد
- هیچ حساب اشتراکی مربوط به شما وجود ندارد
+ هیچ حساب پس انداز مرتبط با شما وجود ندارد
+ هیچ حساب اشتراکی مربوط به شما وجود ندارد
هیچ معاملات مربوط به شما وجود ندارد
تاریخ پایان
تاریخ پایان باید بیشتر از تاریخ شروع باشد
@@ -158,11 +158,11 @@
دوباره به عقب فشار دهید تا خارج شوید
تاریخ پرداخت پیش بینی شده
سوالات متداول
- کسر هزینه
+ کسر هزینه
هزینه
هزینه پرداخت
واگذاری مشتری
- فیلتر
+ فیلتر
حساب های قرضه را فیلتر کنید
حساب های پس انداز را فیلتر کنید
حساب اشتراکی را فیلتر کنید
@@ -181,7 +181,7 @@
راهنما در دست ساخت
خانه
وارد QR
- در عقب افتادگی ها
+ در عقب افتادگی ها
غیر فعال
غیر فعال
وام تولید درآمد
@@ -191,7 +191,7 @@
دوره محاسبه ای سود
نرخ سود متهم شد
سود پرداخت شد
- ارسال علاقه
+ ارسال علاقه
امتناع کرد از سود
لطفاً مطمئن شوید که به اینترنت متصل هستید
مقدار نامعتبر است
@@ -203,9 +203,9 @@
آخرین جزئیات معامله
آخرین معامله
یک ایمیل بگذارید
- مجوزها
- قرضه
- حساب قرضه
+ مجوزها
+ قرضه
+ حساب قرضه
جزئیات حساب قرضه
حساب قرضه با موفقیت برداشت شد
پرداخت شده
@@ -237,7 +237,7 @@
ذینفعان را مدیریت کنید
خانه به صنعت بزرگ فناوری
سیاتل
- بالغ
+ بالغ
متن متوسط
پیام
حداقل تعادل مورد نیاز
@@ -273,8 +273,8 @@
کشوی باز
دیگر
"مقدار پرداخت نا شده "
- هزینه اضافه برداشت
- اضافه پرداخت
+ هزینه اضافه برداشت
+ اضافه پرداخت
کد عبور
کد عبور مطابقت ندارد.
یک رمز عبور برای ورود به سیستم تنظیم کنید
@@ -287,7 +287,7 @@
پرداخت به
مجازات
مجازات برداشته شدند
- در انتظار
+ در انتظار
اجازه استفاده از دوربین رد شد
اجازه ذخیره سازی رد شد
شماره تلفن
@@ -297,7 +297,7 @@
" اصلی"
مقدار اصلی*
اصلی پرداخت شده
- سیاست حفظ حریم خصوصی
+ سیاست حفظ حریم خصوصی
ادامه دهید
تولید - محصول
بارگیری
@@ -313,7 +313,7 @@
تازه کردن
ثبت نام
رد شد
- انتقال رد شد
+ انتقال رد شد
اظهار نظریه
اظهار نظر اجباری است
بازپرداخت
@@ -327,8 +327,8 @@
معاملات در حساب صرفه جوی
" حساب پس انداز با موفقیت به روز شد "
نام محصول
- پس انداز
- حساب پس انداز
+ پس انداز
+ حساب پس انداز
معاملات حساب پس انداز
حساب پس انداز برداشت موفقیت آمیز است
هزینه های پس انداز
@@ -343,10 +343,10 @@
برای پرداخت به حساب خود را انتخاب کنید
شناسه محصول را انتخاب کنید *
منطقه ای را با کد QR انتخاب کنید
- تمام آنچه را که می خواهید درخواست کنید را انتخاب کنید
+ تمام آنچه را که می خواهید درخواست کنید را انتخاب کنید
تنظیمات
- اشتراک گذاری
- حساب اشتراکی
+ اشتراک گذاری
+ حساب اشتراکی
کد QR را به اشتراک بگذارید
در انتظار
ثبت نام
@@ -359,7 +359,7 @@
ارسال
ارسال ذینفع
" ارسال قرض"
- ارسال شده
+ ارسال شده
نمایش یا پنهان کردن کل مبلغ پس انداز
نمایش یا پنهان کردن کل مبلغ وام
*****
@@ -413,14 +413,14 @@
مشاهده برنامه بازپرداخت
معاملات را مشاهده کنید
در انتظار
- منتظر اعزام
+ منتظر اعزام
برداشت قرضه
دلیل برداشت قرضه
برداشت حساب پس انداز
- برداشت از حساب
+ برداشت از حساب
تاریخ برداشت
- انتقال برداشت
- برداشت
+ انتقال برداشت
+ برداشت
کار کردن
بلي
اطلاعات برنامه
diff --git a/androidApp/src/main/res/values-fr/strings.xml b/androidApp/src/main/res/values-fr/strings.xml
index e4d32e82b..212ee6be7 100644
--- a/androidApp/src/main/res/values-fr/strings.xml
+++ b/androidApp/src/main/res/values-fr/strings.xml
@@ -14,9 +14,9 @@
Transactions récentes
Honoraires
Questionnaire
- À propos de nous
+ À propos de nous
Enregistrer les détails du compte
- Compte d\'épargne
+ Compte d\'épargne
Erreur lors du chargement des informations du compte de crédit
Taux d\'intérêt nominaux
Numéro de compte
@@ -29,11 +29,11 @@
Petit texte
Comptes clients
Écran de bienvenue
- Économie
- Prêt
- Compte créditeur
- Part
- Partagez votre compte
+ Économie
+ Prêt
+ Compte créditeur
+ Part
+ Partagez votre compte
Choisissez un client
Travail
Image de l\'état
@@ -48,8 +48,8 @@
Prudence
Date de paiement attendue
Date limite de soumission
- Uploads
- Frais
+ Uploads
+ Frais
Date de transfert
Demander un prêt
Mettre à jour le prêt
@@ -65,23 +65,23 @@
Stratégie de remboursement
Paiement à
Paiement de
- Annuler
+ Annuler
Avant-première
Transférer à
Transfert de
Faire un transfert
- Dépôt
+ Dépôt
Entrez la quantité
La note est obligatoire
- Approuvé
+ Approuvé
Par anticipation
Payé
Équilibre
Rejeté
Attente
- Surpayé
- En retard
- Choisissez tout ce que vous voulez appliquer
+ Surpayé
+ En retard
+ Choisissez tout ce que vous voulez appliquer
Filtrer les comptes d\'épargne
Filtrer les comptes de crédit
Filtrer le partage de compte
@@ -97,12 +97,12 @@
Virement d\'épargne
Frais de prêt
Épargnes
- Actif
- Fermé
- Retiré
+ Actif
+ Fermé
+ Retiré
Nécessite l\'approbation
- Par anticipation
- Mature
+ Par anticipation
+ Mature
Créer un compte
Nom
Nom
@@ -133,9 +133,9 @@
Le compte doit être actif pour effectuer un dépôt
Le compte doit être actif pour effectuer un transfert
- Il n\'y a pas de compte d\'épargne lié à vous
- Aucun compte de prêt ne vous est associé.
- Aucun compte partagé ne vous est associé.
+ Il n\'y a pas de compte d\'épargne lié à vous
+ Aucun compte de prêt ne vous est associé.
+ Aucun compte partagé ne vous est associé.
Il n\'y a aucune transaction liée à vous
Aucun calendrier de remboursement ne vous est associé.
Aucune transaction disponible
@@ -225,8 +225,8 @@
Calendrier de remboursement
Transactions
Transfert
- Approbation en cours
- En attente de paiement
+ Approbation en cours
+ En attente de paiement
Fermé en raison d\'obligations
Prêt fermé
Par date:
@@ -246,7 +246,7 @@
4 semaines
Trois mois
6 mois
- Filtre
+ Filtre
Date de début
Date de fin
Filtré
@@ -367,7 +367,7 @@
Laisser un message électronique
Trouver des lieux
Aucun paiement
- Effacer les filtres
+ Effacer les filtres
Gérer les comptes
Plus
Paramètres
@@ -405,7 +405,7 @@
Le mot de passe ne correspond pas.
Version %1$s
Tous droits réservés.
- Licence
+ Licence
Impossible d\'effectuer un transfert, veuillez réessayer ultérieurement
S\'il vous plaît attendez…
Message
diff --git a/androidApp/src/main/res/values-hi/strings.xml b/androidApp/src/main/res/values-hi/strings.xml
index 8f9c3a10e..6df2e0a64 100644
--- a/androidApp/src/main/res/values-hi/strings.xml
+++ b/androidApp/src/main/res/values-hi/strings.xml
@@ -1,6 +1,6 @@
- Mifos Mobile
+ Mifos Mobile
लॉग इन करें
स्वागत हे %1$s
इंटरनेट से कनेक्ट नहीं है
@@ -16,9 +16,9 @@
हाल ही के लेनदेन
प्रभार
प्रश्नावली
- हमारे बारे में
+ हमारे बारे में
बचत खाते का विवरण
- बचत खाता
+ बचत खाता
ऋण खातों के विवरण में लोड करने में त्रुटि
मामूली ब्याज दर
खाता बनाएं
@@ -32,16 +32,16 @@
Small Text
ग्राहक खाते
Splash
- बचत
- ऋण
- ऋण खाता
+ बचत
+ ऋण
+ ऋण खाता
बचत प्रभार
- शेयर
- शेयर खाता
+ शेयर
+ शेयर खाता
एक ग्राहक चुनें
काम कर रहे
- जमा किया गया
- अदायगी
+ जमा किया गया
+ अदायगी
स्थिति छवि
पुनः भुगतान कार्यक्रम
पिछले लेनदेन
@@ -75,17 +75,17 @@
चुकौती रणनीति
को भुगतान करें
से भुगतान करें
- रद्द करें
+ रद्द करें
ट्रांसफर की समीक्षा
के लिए ट्रांसफर
इससे ट्रांसफर करें
ट्रांसफर कर रहे हैं
- जमा
+ जमा
राशी डालें
ट्रांसफर के लिए टिप्पणी दर्ज करें
लाभार्थी का चयन करें
टिप्पणी अनिवार्य है
- मंजूर
+ मंजूर
अपूर्ण
भुगतान किया गया
शेष
@@ -93,24 +93,24 @@
इंतज़ार
रजिस्टर
अनुरोध आईडी
- अधिक भुगतान
- बकाया राशि में
- उन सभी का चयन करें जिन्हें आप आवेदन करना चाहते हैं
+ अधिक भुगतान
+ बकाया राशि में
+ उन सभी का चयन करें जिन्हें आप आवेदन करना चाहते हैं
बचत खाते को फ़िल्टर करें
ऋण खातों को फ़िल्टर करें
शेयर खाते फ़िल्टर करें
खोजें
ट्रांसफर टाइप चुनें
- सक्रिय
- बन्द है
+ सक्रिय
+ बन्द है
अनुमोदन आवश्यक
- अपूर्ण
- परिपक्व
+ अपूर्ण
+ परिपक्व
आगे बढ़ें
बंद करें
बचत खाते में स्थानांतरण
बचत खाते से स्थानांतरण
- Withdrawn
+ Withdrawn
क्यूआर आयात करें
प्रमाणीकरण चिह्न
@@ -149,9 +149,9 @@
नहीं
मिलाx
- आपके पास कोई बचत खाता नहीं है
- आपके पास कोई ऋण खाता नहीं है
- आपके साथ कोई साझा खाता नहीं है
+ आपके पास कोई बचत खाता नहीं है
+ आपके पास कोई ऋण खाता नहीं है
+ आपके साथ कोई साझा खाता नहीं है
आपके साथ कोई लेन-देन जुड़े नहीं है
आपके साथ संबद्ध कोई भी पुनर्भुगतान शेड्यूल नहीं है
कोई और लेनदेन उपलब्ध नहीं है
@@ -280,8 +280,8 @@
पुनः भुगतान कार्यक्रम
लेन-देन
ट्रांसफर
- स्वीकृति लंबित
- व्यय की प्रतीक्षा की जा रही है
+ स्वीकृति लंबित
+ व्यय की प्रतीक्षा की जा रही है
कुछ दायित्वों के कारण बंद
ऋण बंद
नियत तारीख:
@@ -305,7 +305,7 @@
4 सप्ताह
3 महीने
6 महीने
- फिल्टर
+ फिल्टर
आरंभ करने की तिथि
अंतिम तिथि
फ़िल्टर दकर िया गया
@@ -407,7 +407,7 @@
नमस्ते, %1$s
%1$.2f %2$s
- %1$s %2$s
+ %1$s %2$s
%1$s: %2$.2f
%1$s: %2$d
अवैध राशि
@@ -435,7 +435,7 @@
कैमरे का उपयोग करने के लिए अनुमति अस्वीकृत
No withdrawals
- फ़िल्टर हटाएँ
+ फ़िल्टर हटाएँ
सूचनाएं
आधार यूआरएल
किराएदार
@@ -460,10 +460,10 @@
Confirm Password
Password doesnot match.
Version %1$s
- ©2016-%1$s Mifos Initiative.
+ ©2016-%1$s Mifos Initiative.
All rights reserved.
- Licenses
- गोपनीयता नीति
+ Licenses
+ गोपनीयता नीति
पासकोड बदलें
ऐप पासवर्ड बदलें
पासवर्ड बदलें
@@ -518,13 +518,13 @@
विवरण
उत्पाद
- लाभांश भुगतान
- निकासी
- रुचि पोस्टिंग
- शुल्क कटौती
- निकासी स्थानांतरण
- अस्वीकृत स्थानांतरण
- ओवरड्राफ्ट शुल्क
+ लाभांश भुगतान
+ निकासी
+ रुचि पोस्टिंग
+ शुल्क कटौती
+ निकासी स्थानांतरण
+ अस्वीकृत स्थानांतरण
+ ओवरड्राफ्ट शुल्क
बचत खाता लेनदेन
लेन-देन की अवधि
सौदे का प्रकार
diff --git a/androidApp/src/main/res/values-in/strings.xml b/androidApp/src/main/res/values-in/strings.xml
index 3ae88b999..af697ed4f 100644
--- a/androidApp/src/main/res/values-in/strings.xml
+++ b/androidApp/src/main/res/values-in/strings.xml
@@ -14,9 +14,9 @@
Transaksi terbaru
Biaya
Daftar pertanyaan
- Tentang kami
+ Tentang kami
Menyimpan detail akun
- Rekening Tabungan
+ Rekening Tabungan
Kesalahan memuat informasi akun kredit
Suku bunga nominal
Nomor akun
@@ -29,11 +29,11 @@
Teks kecil
Akun pelanggan
Layar selamat datang
- Hemat
- Pinjaman
- Akun kredit
- Saham
- Bagikan akun Anda
+ Hemat
+ Pinjaman
+ Akun kredit
+ Saham
+ Bagikan akun Anda
Pilih pelanggan
Kerja
Citra negara
@@ -48,8 +48,8 @@
Hati-hati
Tanggal pembayaran yang diharapkan
Batas waktu untuk pengiriman
- Upload
- Biaya
+ Upload
+ Biaya
Tanggal transfer
Ajukan pinjaman
Perbarui pinjaman
@@ -65,23 +65,23 @@
Strategi pembayaran kembali
Pembayaran ke
Pembayaran dari
- Membatalkan
+ Membatalkan
Preview
Transfer ke
Transfer dari
Melakukan transfer
- Deposito
+ Deposito
Masukkan jumlahnya
Catatan adalah wajib
- Disetujui
+ Disetujui
Dalam antisipasi
Dibayar
Keseimbangan
Ditolak
Menunggu
- Membayar lebih
- Tunggakan
- Pilih semua yang ingin Anda terapkan
+ Membayar lebih
+ Tunggakan
+ Pilih semua yang ingin Anda terapkan
Filter rekening tabungan
Filter akun kredit
Filter berbagi akun
@@ -97,12 +97,12 @@
Transfer dari tabungan
Biaya pinjaman
Tabungan
- Aktif
- Tertutup
- Pendiam
+ Aktif
+ Tertutup
+ Pendiam
Membutuhkan persetujuan
- Dalam antisipasi
- Dewasa
+ Dalam antisipasi
+ Dewasa
Buat akun
Nama
Nama
@@ -133,9 +133,9 @@
Akun harus aktif untuk melakukan setoran
Akun harus aktif untuk melakukan transfer
- Tidak ada rekening tabungan yang terhubung dengan Anda
- Tidak ada rekening pinjaman yang terkait dengan Anda
- Tidak ada akun bersama yang dikaitkan dengan Anda
+ Tidak ada rekening tabungan yang terhubung dengan Anda
+ Tidak ada rekening pinjaman yang terkait dengan Anda
+ Tidak ada akun bersama yang dikaitkan dengan Anda
Tidak ada transaksi yang terkait dengan Anda
Tidak ada jadwal pembayaran kembali yang terkait dengan Anda
Tidak ada transaksi yang tersedia
@@ -225,8 +225,8 @@
Jadwal pembayaran kembali
Transaksi
Transfer
- Persetujuan sedang berlangsung
- Menunggu pembayaran
+ Persetujuan sedang berlangsung
+ Menunggu pembayaran
Ditutup karena beberapa kewajiban
Pinjaman ditutup
Menurut tanggal:
@@ -247,7 +247,7 @@
4 minggu
Tiga bulan
6 bulan
- Filter
+ Filter
Tanggal mulai
Tanggal berakhir
Tersaring
@@ -369,7 +369,7 @@
Tinggalkan pesan e-mail
Temukan lokasi
Tidak ada pembayaran
- Hapus filter
+ Hapus filter
Kelola akun
Lebih
Pengaturan
@@ -407,7 +407,7 @@
Kata Sandi Tidak cocok.
Versi %1$s
Hak cipta dilindungi.
- Perizinan
+ Perizinan
Tidak dapat melakukan transfer, silakan coba lagi nanti
Tolong tunggu…
Pesan
diff --git a/androidApp/src/main/res/values-km/strings.xml b/androidApp/src/main/res/values-km/strings.xml
index 8f98e003e..3bd44e730 100644
--- a/androidApp/src/main/res/values-km/strings.xml
+++ b/androidApp/src/main/res/values-km/strings.xml
@@ -14,9 +14,9 @@
ប្រតិបត្តិការថ្មីៗ
ថ្លៃសេវា
សំណួរ
- អំពីយើង
+ អំពីយើង
ការរក្សាទុកព័ត៌មានលម្អិតគណនី
- គណនីសន្សំ
+ គណនីសន្សំ
កំហុសក្នុងការផ្ទុកព័ត៌មានគណនីឥណទាន
អត្រាការប្រាក់នាមករណ៍
លេខគណនី
@@ -29,11 +29,11 @@
អត្ថបទតូច
គណនីអតិថិជន
អេក្រង់ស្វាគមន៍
- ការរក្សាទុក
- ប្រាក់កម្ចី
- គណនីឥណទាន
- ចែករំលែក
- ចែករំលែកគណនីរបស់អ្នក
+ ការរក្សាទុក
+ ប្រាក់កម្ចី
+ គណនីឥណទាន
+ ចែករំលែក
+ ចែករំលែកគណនីរបស់អ្នក
ជ្រើសរើសអតិថិជន
ធ្វើការ
រូបភាពរបស់រដ្ឋ
@@ -48,8 +48,8 @@
ការប្រុងប្រយ័ត្ន
កាលបរិច្ឆេទទូទាត់ដែលរំពឹងទុក
ថ្ងៃផុតកំណត់សម្រាប់ការដាក់ស្នើ
- ផ្ទុកឡើង
- ចំណាយ
+ ផ្ទុកឡើង
+ ចំណាយ
កាលបរិច្ឆេទផ្ទេរ
ដាក់ពាក្យសុំប្រាក់កម្ចី
ធ្វើបច្ចុប្បន្នភាពប្រាក់កម្ចី
@@ -65,23 +65,23 @@
យុទ្ធសាស្ត្រសងប្រាក់
ការទូទាត់ទៅ
ការទូទាត់ពី
- បោះបង់
+ បោះបង់
មើលជាមុន
ផ្ទេរទៅ
ផ្ទេរពី
ធ្វើការផ្ទេរ
- ការដាក់ប្រាក់
+ ការដាក់ប្រាក់
បញ្ចូលបរិមាណ
ចំណាំគឺជាកាតព្វកិច្ច
- បានអនុម័ត
+ បានអនុម័ត
នៅក្នុងការស្មានទុកជាមុន
បានបង់ប្រាក់
តុល្យភាព
ច្រានចោល
ការរង់ចាំ
- ប្រាក់
- នៅបំណុល
- ជ្រើសរើសអ្វីគ្រប់យ៉ាងដែលអ្នកចង់អនុវត្ត
+ ប្រាក់
+ នៅបំណុល
+ ជ្រើសរើសអ្វីគ្រប់យ៉ាងដែលអ្នកចង់អនុវត្ត
ត្រងគណនីសន្សំ
ត្រងគណនីឥណទាន
តម្រងការចែករំលែកគណនី
@@ -97,12 +97,12 @@
ផ្ទេរពីការសន្សំ
កម្រៃសម្រាប់ប្រាក់កម្ចី
សន្សំ
- សកម្ម
- បិទ
- ដក
+ សកម្ម
+ បិទ
+ ដក
តម្រូវឱ្យមានការយល់ព្រម
- នៅក្នុងការស្មានទុកជាមុន
- ចាស់ទុំ
+ នៅក្នុងការស្មានទុកជាមុន
+ ចាស់ទុំ
បង្កើតគណនី
ឈ្មោះ
ឈ្មោះ
@@ -133,9 +133,9 @@
គណនីគួរតែសកម្មដើម្បីដាក់ប្រាក់
គណនីគួរតែសកម្មដើម្បីធ្វើការផ្ទេរ
- មិនមានគណនីសន្សំមានទំនាក់ទំនងជាមួយអ្នកទេ
- មិនមានគណនីប្រាក់កម្ចីដែលជាប់ទាក់ទងជាមួយអ្នកទេ
- មិនមានគណនីចែករំលែកត្រូវបានភ្ជាប់ជាមួយអ្នកទេ
+ មិនមានគណនីសន្សំមានទំនាក់ទំនងជាមួយអ្នកទេ
+ មិនមានគណនីប្រាក់កម្ចីដែលជាប់ទាក់ទងជាមួយអ្នកទេ
+ មិនមានគណនីចែករំលែកត្រូវបានភ្ជាប់ជាមួយអ្នកទេ
មិនមានប្រតិបត្តិការទាក់ទងនឹងអ្នកទេ
មិនមានកាលវិភាគសងប្រាក់ដែលជាប់ទាក់ទងជាមួយអ្នកទេ
មិនមានប្រតិបត្តិការទេ
@@ -225,8 +225,8 @@
កាលវិភាគសងប្រាក់
ប្រតិបតិ្តការ
ការផ្ទេរប្រាក់
- ការអនុម័តនៅក្នុងដំណើរការ
- រង់ចាំការទូទាត់
+ ការអនុម័តនៅក្នុងដំណើរការ
+ រង់ចាំការទូទាត់
បានបិទដោយសារតែកាតព្វកិច្ចមួយចំនួន
ឥណទានបានបិទ
តាមកាលបរិច្ឆេទ:
@@ -247,7 +247,7 @@
4 សប្តាហ៍
បីខែ
6 ខែ
- តម្រង
+ តម្រង
កាលបរិច្ឆេទចាប់ផ្ដើម
កាលបរិច្ឆេទបញ្ចប់
ត្រង
@@ -369,7 +369,7 @@
ទុកសារអេឡិចត្រូនិច
រកទីតាំង
គ្មានការទូទាត់
- ជម្រះតម្រង
+ ជម្រះតម្រង
គ្រប់គ្រងគណនី
ច្រើនទៀត
ការកំណត់
@@ -407,7 +407,7 @@
ពាក្យសម្ងាត់មិនត្រូវគ្នា។
កំណែ %1$s
រក្សាសិទ្ធិគ្រប់យ៉ាង។
- អាជ្ញាបណ្ណ
+ អាជ្ញាបណ្ណ
មិនអាចផ្ទេរបានសូមព្យាយាមម្តងទៀតពេលក្រោយ
សូមរង់ចាំ…
សារ
diff --git a/androidApp/src/main/res/values-kn/strings.xml b/androidApp/src/main/res/values-kn/strings.xml
index fe5a70411..57f2705a5 100644
--- a/androidApp/src/main/res/values-kn/strings.xml
+++ b/androidApp/src/main/res/values-kn/strings.xml
@@ -14,9 +14,9 @@
ಇತ್ತೀಚಿನ ವಹಿವಾಟುಗಳು
ಶುಲ್ಕ
ಪ್ರಶ್ನಾವಳಿಯನ್ನು
- ನಮ್ಮ ಬಗ್ಗೆ
+ ನಮ್ಮ ಬಗ್ಗೆ
ಖಾತೆ ವಿವರಗಳನ್ನು ಉಳಿಸಲಾಗುತ್ತಿದೆ
- ಉಳಿತಾಯ ಖಾತೆ
+ ಉಳಿತಾಯ ಖಾತೆ
ಕ್ರೆಡಿಟ್ ಖಾತೆ ಮಾಹಿತಿಯನ್ನು ಲೋಡ್ ಮಾಡುವಲ್ಲಿ ದೋಷ
ನಾಮಮಾತ್ರ ಬಡ್ಡಿ ದರಗಳು
ಖಾತೆ ಸಂಖ್ಯೆ
@@ -29,11 +29,11 @@
ಸಣ್ಣ ಪಠ್ಯ
ಗ್ರಾಹಕ ಖಾತೆಗಳು
ಸ್ವಾಗತ ಪರದೆಯ
- ಉಳಿಸುವ
- ಸಾಲ
- ಕ್ರೆಡಿಟ್ ಖಾತೆ
- ಹಂಚಿಕೊಳ್ಳಿ
- ನಿಮ್ಮ ಖಾತೆಯನ್ನು ಹಂಚಿಕೊಳ್ಳಿ
+ ಉಳಿಸುವ
+ ಸಾಲ
+ ಕ್ರೆಡಿಟ್ ಖಾತೆ
+ ಹಂಚಿಕೊಳ್ಳಿ
+ ನಿಮ್ಮ ಖಾತೆಯನ್ನು ಹಂಚಿಕೊಳ್ಳಿ
ಗ್ರಾಹಕರನ್ನು ಆಯ್ಕೆ ಮಾಡಿ
ಕೆಲಸ
ರಾಜ್ಯದ ಚಿತ್ರ
@@ -48,8 +48,8 @@
ಎಚ್ಚರಿಕೆ
ನಿರೀಕ್ಷಿತ ಪಾವತಿಯ ದಿನಾಂಕ
ಸಲ್ಲಿಕೆಗಾಗಿ ಅಂತಿಮ ದಿನಾಂಕ
- ಅಪ್ಲೋಡ್ಗಳನ್ನು
- ವೆಚ್ಚದಲ್ಲಿ
+ ಅಪ್ಲೋಡ್ಗಳನ್ನು
+ ವೆಚ್ಚದಲ್ಲಿ
ವರ್ಗಾವಣೆಯ ದಿನಾಂಕ
ಸಾಲದ ಅರ್ಜಿ
ಸಾಲವನ್ನು ನವೀಕರಿಸಿ
@@ -65,23 +65,23 @@
ಮರುಪಾವತಿ ತಂತ್ರ
ಗೆ ಪಾವತಿ
ನಿಂದ ಪಾವತಿ
- ರದ್ದು
+ ರದ್ದು
ಮುನ್ನೋಟ
ವರ್ಗಾಯಿಸಿ
ರಿಂದ ವರ್ಗಾಯಿಸಿ
ವರ್ಗಾವಣೆ ಮಾಡುವುದು
- ಠೇವಣಿ
+ ಠೇವಣಿ
ಪ್ರಮಾಣವನ್ನು ನಮೂದಿಸಿ
ಗಮನಿಸಿ ಕಡ್ಡಾಯವಾಗಿದೆ
- ಅನುಮೋದನೆ
+ ಅನುಮೋದನೆ
ನಿರೀಕ್ಷೆಯಲ್ಲಿ
ಹಣ
ಸಮತೋಲನ
ತಿರಸ್ಕರಿಸಿದರು
ಕಾಯುವ
- ಓವರ್ಪೇಯ್ಡ್
- ಬಾಕಿಗಳಲ್ಲಿ
- ನೀವು ಅನ್ವಯಿಸಲು ಬಯಸುವ ಎಲ್ಲವನ್ನೂ ಆಯ್ಕೆ ಮಾಡಿ
+ ಓವರ್ಪೇಯ್ಡ್
+ ಬಾಕಿಗಳಲ್ಲಿ
+ ನೀವು ಅನ್ವಯಿಸಲು ಬಯಸುವ ಎಲ್ಲವನ್ನೂ ಆಯ್ಕೆ ಮಾಡಿ
ಫಿಲ್ಟರ್ ಉಳಿತಾಯ ಖಾತೆಗಳು
ಕ್ರೆಡಿಟ್ ಖಾತೆಗಳನ್ನು ಫಿಲ್ಟರ್ ಮಾಡಿ
ಫಿಲ್ಟರ್ ಖಾತೆ ಹಂಚಿಕೆ
@@ -97,12 +97,12 @@
ಉಳಿತಾಯದಿಂದ ವರ್ಗಾವಣೆ
ಸಾಲಗಳಿಗೆ ಶುಲ್ಕ
ಉಳಿತಾಯ
- ಸಕ್ರಿಯ
- ಮುಚ್ಚಲಾಗಿದೆ
- ಹಿಂದಕ್ಕೆ
+ ಸಕ್ರಿಯ
+ ಮುಚ್ಚಲಾಗಿದೆ
+ ಹಿಂದಕ್ಕೆ
ಅನುಮೋದನೆ ಅಗತ್ಯವಿದೆ
- ನಿರೀಕ್ಷೆಯಲ್ಲಿ
- ಪ್ರೌಢ
+ ನಿರೀಕ್ಷೆಯಲ್ಲಿ
+ ಪ್ರೌಢ
ಖಾತೆಯನ್ನು ರಚಿಸಿ
ಹೆಸರು
ಹೆಸರು
@@ -133,9 +133,9 @@
ಠೇವಣಿ ಮಾಡಲು ಖಾತೆ ಸಕ್ರಿಯವಾಗಿರಬೇಕು
ವರ್ಗಾವಣೆ ಮಾಡಲು ಖಾತೆ ಸಕ್ರಿಯವಾಗಿರಬೇಕು
- ನಿಮ್ಮೊಂದಿಗೆ ಯಾವುದೇ ಉಳಿತಾಯ ಖಾತೆಗಳಿಲ್ಲ
- ನಿಮ್ಮೊಂದಿಗೆ ಯಾವುದೇ ಸಾಲ ಖಾತೆಗಳಿಲ್ಲ
- ನಿಮ್ಮೊಂದಿಗೆ ಸಂಯೋಜಿತವಾಗಿರುವ ಯಾವುದೇ ಖಾತೆಗಳಿಲ್ಲ
+ ನಿಮ್ಮೊಂದಿಗೆ ಯಾವುದೇ ಉಳಿತಾಯ ಖಾತೆಗಳಿಲ್ಲ
+ ನಿಮ್ಮೊಂದಿಗೆ ಯಾವುದೇ ಸಾಲ ಖಾತೆಗಳಿಲ್ಲ
+ ನಿಮ್ಮೊಂದಿಗೆ ಸಂಯೋಜಿತವಾಗಿರುವ ಯಾವುದೇ ಖಾತೆಗಳಿಲ್ಲ
ನಿಮಗೆ ಸಂಬಂಧಿಸಿದ ಯಾವುದೇ ವಹಿವಾಟುಗಳಿಲ್ಲ
ನಿಮ್ಮೊಂದಿಗೆ ಯಾವುದೇ ಮರುಪಾವತಿಯ ವೇಳಾಪಟ್ಟಿ ಇಲ್ಲ
ಯಾವುದೇ ವ್ಯವಹಾರ ಲಭ್ಯವಿಲ್ಲ
@@ -225,8 +225,8 @@
ಮರುಪಾವತಿಯ ವೇಳಾಪಟ್ಟಿ
ವ್ಯವಹಾರ
ವರ್ಗಾವಣೆ
- ಅನುಮೋದನೆ ಪ್ರಗತಿಯಲ್ಲಿದೆ
- ಪಾವತಿಗಾಗಿ ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ
+ ಅನುಮೋದನೆ ಪ್ರಗತಿಯಲ್ಲಿದೆ
+ ಪಾವತಿಗಾಗಿ ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ
ಕೆಲವು ಜವಾಬ್ದಾರಿಗಳಿಂದಾಗಿ ಮುಚ್ಚಲಾಗಿದೆ
ಸಾಲ ಮುಚ್ಚಲಾಗಿದೆ
ದಿನಾಂಕದಂದು:
@@ -247,7 +247,7 @@
4 ವಾರಗಳು
ಮೂರು ತಿಂಗಳು
6 ತಿಂಗಳು
- ಫಿಲ್ಟರ್
+ ಫಿಲ್ಟರ್
ಪ್ರಾರಂಭ ದಿನಾಂಕ
ಅಂತ್ಯದ ದಿನಾಂಕ
ಫಿಲ್ಟರ್
@@ -369,7 +369,7 @@
ಇ-ಮೇಲ್ ಸಂದೇಶವನ್ನು ಬಿಡಿ
ಸ್ಥಳಗಳನ್ನು ಹುಡುಕಿ
ಪಾವತಿಗಳು ಇಲ್ಲ
- ಫಿಲ್ಟರ್ಗಳನ್ನು ತೆರವುಗೊಳಿಸಿ
+ ಫಿಲ್ಟರ್ಗಳನ್ನು ತೆರವುಗೊಳಿಸಿ
ಖಾತೆಗಳನ್ನು ನಿರ್ವಹಿಸಿ
ಹೆಚ್ಚು
ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು
@@ -407,7 +407,7 @@
ಪಾಸ್ವರ್ಡ್ ಹೊಂದುತ್ತಿಲ್ಲ.
ಆವೃತ್ತಿ%1$s
ಎಲ್ಲಾ ಹಕ್ಕುಗಳನ್ನು ಕಾಯ್ದಿರಿಸಲಾಗಿದೆ.
- ಪರವಾನಗಿ
+ ಪರವಾನಗಿ
ವರ್ಗಾವಣೆ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ, ದಯವಿಟ್ಟು ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ
ದಯವಿಟ್ಟು ನಿರೀಕ್ಷಿಸಿ…
ಸಂದೇಶ
diff --git a/androidApp/src/main/res/values-my/strings.xml b/androidApp/src/main/res/values-my/strings.xml
index 02cf85e6a..d9f033632 100644
--- a/androidApp/src/main/res/values-my/strings.xml
+++ b/androidApp/src/main/res/values-my/strings.xml
@@ -1,6 +1,6 @@
- Mifos မိုဘိုင်း
+ Mifos မိုဘိုင်း
လော့ဂ်အင်
%1$s ကိုမှလှိုက်လှဲစွာကြိုဆိုပါသည်
အင်တာနက်ချိတ်ဆက်မ
@@ -16,9 +16,9 @@
မကြာမီကအရောင်းအဝယ်
စွဲချက်
မေးခွန်းပုံစံ
- ကျွန်ုပ်တို့အကြောင်း
+ ကျွန်ုပ်တို့အကြောင်း
အကောင့်အသေးစိတ်ကိုသိမ်းဆည်းနေ
- ငွေစုဘဏ်စာရင်း
+ ငွေစုဘဏ်စာရင်း
ချေးငွေအတွက်တင်ပေးမှုအမှားအသေးစိတ်အကောင့်
အမည်ခံအတိုးနှုန်း
အကောင့်နံပါတ်
@@ -31,11 +31,11 @@
အသေးစားစာသား
client Accounts ကို
ရေပက်
- သိမ်းဆည်းနေ
- ခြေးငှေ
- ချေးငွေအကောင့်
- ဝေစု
- ဝေမျှမယ်အကောင့်
+ သိမ်းဆည်းနေ
+ ခြေးငှေ
+ ချေးငွေအကောင့်
+ ဝေစု
+ ဝေမျှမယ်အကောင့်
တစ်ဦးကို client ကို ရွေးချယ်.
အလုပ်အဖွဲ့
အဆင့်အတန်းပုံရိပ်
@@ -50,8 +50,8 @@
ပွောဆို
မျှော်လင့်ထားသည့်ငွေပေးချေနေ့စွဲ
တင်သွင်းခဲ့တဲ့နေ့စွဲ
- Submitted
- ငွေပေးချေ
+ Submitted
+ ငွေပေးချေ
လွှဲပြောင်းနေ့စွဲ
ချေးငွေအဘို့အ Apply
Update ကိုချေးငွေ
@@ -67,23 +67,23 @@
ပြန်ဆပ်မဟာဗျူဟာ
ရန်ပေးဆောင်
မှစ. ပေးဆောင်
- Cancel
+ Cancel
ဆန်းစစ်ခြင်း
ရန်လွှဲပြောင်း
မှစ. လွှဲပြောင်း
လွှဲပြောင်း Making
- အပျငှေ
+ အပျငှေ
ငွေပမာဏကိုရိုက်ထည့်
မှတ်ချက်မဖြစ်မနေဖြစ်ပါသည်
- approved
+ approved
လာမည့်
paid
ချိန်ခွင်လျှာ
ပယ်ချ
စောင့်ဆိုင်းနေ
- Overpaid
- ကြွေးကျန်အတွက်
- သငျသညျလျှောက်ထားချင်အားလုံးကို Select လုပ်ပါ
+ Overpaid
+ ကြွေးကျန်အတွက်
+ သငျသညျလျှောက်ထားချင်အားလုံးကို Select လုပ်ပါ
ချွေတာသုံးစွဲ Accounts ကို Filter
ချေးငွေ Accounts ကို Filter
filter ဝေမျှမယ် Accounts ကို
@@ -99,12 +99,12 @@
ချွေတာသုံးစွဲရာမှလွှဲပြောင်း
ချေးငွေစွပ်စွဲချက်
ချွေတာသုံးစွဲစွပ်စွဲချက်
- active
- ပိတ်သိမ်း
- ဆုတ်ခွာ
+ active
+ ပိတ်သိမ်း
+ ဆုတ်ခွာ
အတည်ပြုချက်လိုအပ်
- လာမည့်
- ရင့်ကျက်
+ လာမည့်
+ ရင့်ကျက်
အကောင့်တစ်ခုဖန်တီးသည်
နာမည်
မျိုးနွယ်အမည်
@@ -135,9 +135,9 @@
အကောင့်တစ်ခုဖျော်ဖြေဖို့ Active ကိုဖြစ်သင့်တယ်အပျငှေ
အကောင့်တစ်ခုဖျော်ဖြေဖို့ Active ကိုဖြစ်သင့်တယ်လွှဲပြောင်း
- သင်နှင့်အတူဆက်နွယ်ခြင်းမရှိ SavingsAccount ရှိပါတယ်
- သင်နှင့်အတူဆက်နွယ်ခြင်းမရှိ LoanAccount ရှိပါတယ်
- သင်နှင့်အတူဆက်နွယ်ခြင်းမရှိ ShareAccount ရှိပါတယ်
+ သင်နှင့်အတူဆက်နွယ်ခြင်းမရှိ SavingsAccount ရှိပါတယ်
+ သင်နှင့်အတူဆက်နွယ်ခြင်းမရှိ LoanAccount ရှိပါတယ်
+ သင်နှင့်အတူဆက်နွယ်ခြင်းမရှိ ShareAccount ရှိပါတယ်
သင်နှင့်အတူဆက်နွယ်ခြင်းမရှိငွေကြေးလွှဲပြောင်းမှုမှာရှိပါတယ်
သင်နှင့်အတူဆက်နွယ်ခြင်းမရှိပြန်ဆပ်အချိန်ဇယားရှိပါတယ်
ရရှိနိုင်ပါကပိုအရောင်းအဘယ်သူမျှမက
@@ -228,8 +228,8 @@
ပြန်ဆပ်ဇယား
အရောင်းအ
လွှဲပြောင်း
- ခွင့်ပြုချက်ဆိုင်းငံ့ထား
- ငွေပေးချေဘို့စောင့်ဆိုင်းနေ
+ ခွင့်ပြုချက်ဆိုင်းငံ့ထား
+ ငွေပေးချေဘို့စောင့်ဆိုင်းနေ
ဘာဖြစ်လို့လဲဆိုတော့တချို့တာဝန်၏အပိတ်
ချေးငွေပိတ်သည်
နောက်ဆုံးရက်:
@@ -253,7 +253,7 @@
၄ သီတင်းပါတ်
၃ လ
၆ လ
- ရေစစ်
+ ရေစစ်
စတင်သည့်ရက်စွဲ
အဆုံးနေ့စွဲ
စစ်ထုတ်သည်
@@ -366,7 +366,7 @@
မင်္ဂလာပါ %1$s ကို
%1$.2f
%1$.2f %2$s ကို
- %1$s ကို %2$s ကို
+ %1$s ကို %2$s ကို
%1$s ကို %2$.2f
%1$s ကို: %2$ ဃ
မှားနေသောငွေပမာဏ
@@ -390,7 +390,7 @@
အီးမေးလ်တစ်စောင် Leave
တည်နေရာကိုရှာပါ
အဘယ်သူမျှမထုတ်ယူ
- Clear ကိုစိစစ်မှုများ
+ Clear ကိုစိစစ်မှုများ
Manage Accounts ကို
နောက်ထပ်
Settings များ
@@ -427,9 +427,9 @@
စကားဝှက်ကိုအတည်ပြုပါ
Password ကိုမကိုက်ညီပါဘူး။
ဗားရှင်း %1$s ကို
- ©2016-%1$s Mifos Initiative.
+ ©2016-%1$s Mifos Initiative.
မူပိုင်ခွင့်များရယူပြီး။
- လိုင်စင်
+ လိုင်စင်
လွှဲပြောင်းဖြည့်စွက်ဖို့မအောင်မြင်ဘူး, နောက်မှာထပ်ကြိုးစားကြည့်ပါ
ကျေးဇူးပြုပြီးခဏစောင့်ပါ…
မက်ဆေ့ခ်ျကို
diff --git a/androidApp/src/main/res/values-pl/strings.xml b/androidApp/src/main/res/values-pl/strings.xml
index ffadcfa0d..2a798576a 100644
--- a/androidApp/src/main/res/values-pl/strings.xml
+++ b/androidApp/src/main/res/values-pl/strings.xml
@@ -14,9 +14,9 @@
Ostatnie transakcje
Opłaty
Ankieta
- O nas
+ O nas
Zapisywanie szczegółów konta
- Konto oszczędnościowe
+ Konto oszczędnościowe
Błąd podczas ładowania danych konta kredytowego
Nominalne stopy procentowe
Numer konta
@@ -29,11 +29,11 @@
Mały tekst
Konta klientów
Ekran powitalny
- Oszczędność
- Pożyczka
- Konto kredytowe
- Udostępnij
- Udostępnij konto
+ Oszczędność
+ Pożyczka
+ Konto kredytowe
+ Udostępnij
+ Udostępnij konto
Wybierz klienta
Pracujący
Obraz stanu
@@ -48,8 +48,8 @@
Uwaga
Oczekiwana data wypłaty
Termin składania
- Przesłane
- Wydatek
+ Przesłane
+ Wydatek
Data przelewu
Złóż wniosek o pożyczkę
Zaktualizuj pożyczkę
@@ -65,23 +65,23 @@
Strategia spłaty
Wpłata do
Wpłata od
- Anuluj
+ Anuluj
Podgląd
Przelew do
Przelew od
Dokonywanie przelewu
- Kaucja
+ Kaucja
Wprowadź ilość
Uwaga jest obowiązkowa
- Zatwierdzony
+ Zatwierdzony
W oczekiwaniu
Płatny
Saldo
Odrzucono
Czekanie
- Nadpłacony
- W zaległości
- Wybierz wszystko, co chcesz zastosować
+ Nadpłacony
+ W zaległości
+ Wybierz wszystko, co chcesz zastosować
Filtruj konta oszczędnościowe
Filtruj konta kredytowe
Filtruj udostępnianie kont
@@ -97,12 +97,12 @@
Przelew z oszczędności
Opłaty za pożyczki
Oszczędności
- Aktywny
- Zamknięte
- Wycofane
+ Aktywny
+ Zamknięte
+ Wycofane
Wymaga zatwierdzenia
- W oczekiwaniu
- Dojrzałe
+ W oczekiwaniu
+ Dojrzałe
Utwórz konto
Imię
Nazwisko
@@ -133,9 +133,9 @@
Konto powinno być aktywne, aby dokonać kaucji
Konto powinno być aktywne, aby wykonać przelew
- Brak powiązanych z tobą kont oszczędnościowych
- Brak powiązanych z tobą kont pożyczkowych
- Brak powiązanych z tobą kont współdzielonych
+ Brak powiązanych z tobą kont oszczędnościowych
+ Brak powiązanych z tobą kont pożyczkowych
+ Brak powiązanych z tobą kont współdzielonych
Brak powiązanych z tobą transakcji
Brak powiązanego z tobą harmonogramu spłat
Brak dostępnych transakcji
@@ -225,8 +225,8 @@
Harmonogram spłaty
Transakcje
Przelew
- Zatwierdzenie w toku
- Oczekiwanie na wypłatę
+ Zatwierdzenie w toku
+ Oczekiwanie na wypłatę
Zamknięte z powodu niektórych zobowiązań
Pożyczka zamknięta
Według daty:
@@ -247,7 +247,7 @@
4 tygodnie
3 miesiące
6 miesięcy
- Filtr
+ Filtr
Data rozpoczęcia
Data zakonczenia
Przefiltrowany
@@ -370,7 +370,7 @@
Pozostaw wiadomość e-mail
Znajdź lokalizacje
Brak wypłat
- Wyczyść filtry
+ Wyczyść filtry
Zarządzaj kontami
Więcej
Ustawienia
@@ -408,7 +408,7 @@
Hasło Nie pasuje.
Wersja %1$s
Wszelkie prawa zastrzeżone.
- Licencje
+ Licencje
Nie można wykonać przelewu, spróbuj ponownie później
Proszę czekać…
wiadomość
diff --git a/androidApp/src/main/res/values-pt/strings.xml b/androidApp/src/main/res/values-pt/strings.xml
index 921920efa..e5d74b0f8 100644
--- a/androidApp/src/main/res/values-pt/strings.xml
+++ b/androidApp/src/main/res/values-pt/strings.xml
@@ -1,5 +1,5 @@
- Mifos Mobile
+ Mifos Mobile
Entrar
Bem-vindo %1$s
Não conectado à internet
@@ -15,9 +15,9 @@
Transações recentes
Cobranças
Questionário
- Sobre nós
+ Sobre nós
Detalhes da conta de poupança
- Conta Poupança
+ Conta Poupança
Taxa de Interesse Nominal
Número da conta
Saldo da Conta
@@ -29,11 +29,11 @@
Texto pequeno
Contas de Clientes
esguicho
- Poupança
- Empréstimo
- Conta de Empréstimos
- Partilhar
- Partilhar conta
+ Poupança
+ Empréstimo
+ Conta de Empréstimos
+ Partilhar
+ Partilhar conta
Escolheu um cliente
A trabalhar
Imagem de Status
@@ -48,8 +48,8 @@
Observação
Data de desembolso esperada
Data de submissão
- Submetido
- Desembolso
+ Submetido
+ Desembolso
Data de transferência
Solicitar empréstimo
Atualizar empréstimo
@@ -65,23 +65,23 @@
Estratégia de Reembolso
Pagar para
Pagar da
- Cancelar
+ Cancelar
Rever
Transferir para
Transferir da
Fazendo Transferência
- Depositar
+ Depositar
Digite Montante
Observação é obrigatória
- Aprovado
+ Aprovado
Pendente
Pago
Saldo
Rejeitado
À espera
- Excesso de pagamento
- Em atraso
- Selecione tudo o que você deseja aplicar
+ Excesso de pagamento
+ Em atraso
+ Selecione tudo o que você deseja aplicar
Filtrar Contas Poupança
Filtrar Contas Empréstimo
Filtrar contas de compartilhamento
@@ -97,12 +97,12 @@
Transferir da poupança
Cobranças de empréstimos
Cobranças de poupanças
- Ativo
- Fechado
- Retirada
+ Ativo
+ Fechado
+ Retirada
Necessita de aprovação
- Pendente
- Amadurecido
+ Pendente
+ Amadurecido
Crie uma conta
Primeiro Nome
Último Nome
@@ -136,9 +136,9 @@
Conta deve estar ativa para transferir
- Não existe nenhuma Conta Poupança associada
- Não existe nenhuma Conta de Empréstimo associada
- Não existe nenhuma Conta Partilha associada
+ Não existe nenhuma Conta Poupança associada
+ Não existe nenhuma Conta de Empréstimo associada
+ Não existe nenhuma Conta Partilha associada
Não existem transações associadas
Não existe nenhum cronograma de reembolso associado
Não há mais transações disponíveis
@@ -238,8 +238,8 @@
Cronograma de pagamento
Transações
Transferência
- Aprovação pendente
- À espera de desembolso
+ Aprovação pendente
+ À espera de desembolso
Fechado devido a algumas obrigações
Empréstimo fechado
Data de vencimento:
@@ -265,7 +265,7 @@
4 Semanas
3 Meses
6 Meses
- Filtro
+ Filtro
Data inicial
Data final
Filtrado
@@ -408,7 +408,7 @@
Olá, %1$s
%1$.2f
%1$.2f %2$s
- %1$s%2$s
+ %1$s%2$s
%1$s: %2$.2f
%1$s: %2$d
Quantia inválida
@@ -432,7 +432,7 @@
Deixar um email
Procurar Lugares
Nenhum levantamento
- Limpar os Filtros
+ Limpar os Filtros
Gerir contas
Mais
Definições
@@ -487,9 +487,9 @@
Confirme a palavra-passe
A palavra-passe não coincide.
Versão %1$s
- ©2016-%1$s Inicitiva Mifos.
+ ©2016-%1$s Inicitiva Mifos.
Todos os direitos reservados.
- Licenças
+ Licenças
Não é possível concluir a transferência, por favor, tente novamente mais tarde
Por favor espere…
mensagem
diff --git a/androidApp/src/main/res/values-ru/strings.xml b/androidApp/src/main/res/values-ru/strings.xml
index 5bf402599..d46a6f9d5 100644
--- a/androidApp/src/main/res/values-ru/strings.xml
+++ b/androidApp/src/main/res/values-ru/strings.xml
@@ -14,9 +14,9 @@
Последние транзакции
Сборы
Вопросник
- О КОМПАНИИ
+ О КОМПАНИИ
Сохранение данных учетной записи
- Сберегательный счет
+ Сберегательный счет
Ошибка при загрузке информации об учетной записи
Номинальные процентные ставки
Номер счета
@@ -29,11 +29,11 @@
Маленький текст
Счета клиентов
Экран приветствия
- Экономия
- Ссуда
- Кредитный счет
- Доля
- Поделитесь своим аккаунтом
+ Экономия
+ Ссуда
+ Кредитный счет
+ Доля
+ Поделитесь своим аккаунтом
Выберите клиента
Работа
Изображение государства
@@ -48,8 +48,8 @@
Предосторожность
Ожидаемая дата выплаты
Крайний срок подачи
- Добавления
- Расходы
+ Добавления
+ Расходы
Дата передачи
Подать заявку на кредит
Обновить кредит
@@ -65,23 +65,23 @@
Стратегия погашения
Оплата
Оплата с
- Отменить
+ Отменить
Предварительный просмотр
Передача
Перевод с
Создание перевода
- Депозит
+ Депозит
Введите количество
Примечание обязательное
- Утвержденный
+ Утвержденный
В ожидании
Оплаченный
Баланс
Отвергнуто
Ожидание
- Переплатил
- Задолженность
- Выберите все, что вы хотите применить
+ Переплатил
+ Задолженность
+ Выберите все, что вы хотите применить
Фильтровать сберегательные счета
Фильтрация кредитных счетов
Слияние фильтров
@@ -97,12 +97,12 @@
Переход от сбережений
Сборы за кредиты
Экономия
- Активный
- Закрыто
- Отозваны
+ Активный
+ Закрыто
+ Отозваны
Требуется разрешение
- В ожидании
- Зрелый
+ В ожидании
+ Зрелый
Создать учетную запись
Имя
Имя
@@ -133,9 +133,9 @@
Аккаунт должен быть активным для внесения депозита
Аккаунт должен быть активным, чтобы сделать передачу
- Нет сберегательных счетов, связанных с вами
- С вами не связаны ссудные счета
- С вами не связаны общие аккаунты
+ Нет сберегательных счетов, связанных с вами
+ С вами не связаны ссудные счета
+ С вами не связаны общие аккаунты
Нет транзакций, связанных с вами
График погашения, связанный с вами, не существует
Нет доступных транзакций
@@ -225,8 +225,8 @@
График погашения
Операции
Перевод
- Продвинутое одобрение
- Ожидание платежа
+ Продвинутое одобрение
+ Ожидание платежа
Закрыто из-за некоторых обязательств
Кредит закрыт
По дате:
@@ -247,7 +247,7 @@
4 недели
Три месяца
6 месяцев
- Фильтр
+ Фильтр
Дата начала
Дата окончания
Отфильтрованный
@@ -370,7 +370,7 @@
Оставьте сообщение электронной почты
Поиск местоположений
Нет выплат
- Очистить фильтры
+ Очистить фильтры
Управление аккаунтами
Более
Настройки
@@ -408,7 +408,7 @@
Пароль не соответствует.
Версия %1$s
Все права сохранены.
- Лицензирование
+ Лицензирование
Невозможно осуществить перевод, повторите попытку позже
Пожалуйста, подождите …
Сообщение
diff --git a/androidApp/src/main/res/values-sw/strings.xml b/androidApp/src/main/res/values-sw/strings.xml
index abed6f814..f71df8311 100644
--- a/androidApp/src/main/res/values-sw/strings.xml
+++ b/androidApp/src/main/res/values-sw/strings.xml
@@ -14,9 +14,9 @@
Shughuli za hivi karibuni
Ada
Dodoso
- Kuhusu sisi
+ Kuhusu sisi
Inahifadhi maelezo ya akaunti
- Akaunti ya akiba
+ Akaunti ya akiba
Hitilafu kupakua habari ya akaunti ya mikopo
Vigezo vya riba
Nambari ya Akaunti
@@ -29,11 +29,11 @@
Nakala ndogo
Akaunti ya Wateja
Karibu skrini
- Kuokoa
- Mkopo
- Akaunti ya mikopo
- Kushiriki
- Shiriki akaunti yako
+ Kuokoa
+ Mkopo
+ Akaunti ya mikopo
+ Kushiriki
+ Shiriki akaunti yako
Chagua mteja
Kazi
Picha ya hali
@@ -48,8 +48,8 @@
Tahadhari
Tarehe inayotarajiwa ya kulipa
Mwisho wa kuwasilisha
- Uploads
- Gharama
+ Uploads
+ Gharama
Tarehe ya uhamisho
Tumia mkopo
Sasisha mkopo
@@ -65,23 +65,23 @@
Mkakati wa kulipa
Malipo kwa
Malipo kutoka
- Kufuta
+ Kufuta
Preview
Tuma kwa
Uhamisho kutoka
Kufanya uhamisho
- Amana
+ Amana
Ingiza wingi
Kumbuka ni lazima
- Kupitishwa
+ Kupitishwa
Kwa kutarajia
Kulipwa
Usawa
Kukataliwa
Kusubiri
- Kulipwa zaidi
- Katika madeni
- Chagua kila kitu unachotaka kuomba
+ Kulipwa zaidi
+ Katika madeni
+ Chagua kila kitu unachotaka kuomba
Akaunti za akiba za kuchuja
Chuja akaunti za mikopo
Chuja kugawana akaunti
@@ -97,12 +97,12 @@
Uhamisho kutoka kwa akiba
Malipo ya mikopo
Akiba
- Kazi
- C.
- Kuondoka
+ Kazi
+ C.
+ Kuondoka
Inahitaji idhini
- Kwa kutarajia
- Kukomaa
+ Kwa kutarajia
+ Kukomaa
Unda akaunti
Jina
Jina
@@ -133,9 +133,9 @@
Akaunti inapaswa kuwa hai kufanya amana
Akaunti inapaswa kuwa hai kufanya uhamisho
- Hakuna akaunti za akiba zilizounganishwa na wewe
- Hakuna akaunti za mkopo zilizohusishwa na wewe
- Hakuna akaunti zilizoshirikiwa zinazohusiana na wewe
+ Hakuna akaunti za akiba zilizounganishwa na wewe
+ Hakuna akaunti za mkopo zilizohusishwa na wewe
+ Hakuna akaunti zilizoshirikiwa zinazohusiana na wewe
Hakuna shughuli zinazohusiana na wewe
Hakuna ratiba ya kulipa yanayohusiana na wewe
Hakuna shughuli zinazopatikana
@@ -225,8 +225,8 @@
Ratiba ya kulipa
Shughuli
Uhamisho
- Idhini inaendelea
- Inasubiri malipo
+ Idhini inaendelea
+ Inasubiri malipo
Ilifungwa kwa sababu ya majukumu fulani
Mkopo ulifungwa
Kwa tarehe:
@@ -247,7 +247,7 @@
Wiki 4
Miezi mitatu
Miezi 6
- Chuja
+ Chuja
Tarehe ya kuanza
Tarehe ya kumaliza
Kuchujwa
@@ -369,7 +369,7 @@
Acha ujumbe wa barua pepe
Pata maeneo
Hakuna malipo
- Kuchuja wazi
+ Kuchuja wazi
Dhibiti akaunti
Zaidi
Mazingira
@@ -407,7 +407,7 @@
Nenosiri hailingani.
Toleo la %1$s
Haki zote zimehifadhiwa.
- Leseni
+ Leseni
Haiwezi kufanya uhamisho, tafadhali jaribu tena baadaye
Tafadhali subiri…
Ujumbe
diff --git a/androidApp/src/main/res/values-te/strings.xml b/androidApp/src/main/res/values-te/strings.xml
index df1cca528..fea90700b 100644
--- a/androidApp/src/main/res/values-te/strings.xml
+++ b/androidApp/src/main/res/values-te/strings.xml
@@ -1,8 +1,8 @@
- Mifos Mobile
+ Mifos Mobile
లాగిన్
స్వాగతం %1$s
- మా గురించి
+ మా గురించి
ఇంటర్నెట్కి కనెక్ట్ చేయబడలేదు
ప్రాథమిక
వాడుకరి పేరు
@@ -17,7 +17,7 @@
ఆరోపణలు
ప్రశ్నాపత్రం
పొదుపు ఖాతా వివరాలు
- పొదుపు ఖాతా
+ పొదుపు ఖాతా
రుణ ఖాతాల వివరాలను లోడ్ చేయడంలో లోపం
నామమాత్ర వడ్డీ రేటు
ఖాతా సంఖ్య
@@ -30,11 +30,11 @@
చిన్న వచనం
క్లయింట్ ఖాతాలు
స్క్రీన్ స్వాగతం
- పొదుపు
- ఋణం
- రుణ ఖాతా
- వాటా
- వాటా ఖాతా
+ పొదుపు
+ ఋణం
+ రుణ ఖాతా
+ వాటా
+ వాటా ఖాతా
ఒక క్లయింట్ ఎంచుకోండి
వర్కింగ్
స్థితి చిత్రం
@@ -49,8 +49,8 @@
వ్యాఖ్య
ఊహించిన చెల్లింపు తేదీ
సమర్పణ తేదీ
- సమర్పించిన
- చెల్లించుట
+ సమర్పించిన
+ చెల్లించుట
బదిలీ తేదీ
రుణ కోసం దరఖాస్తు
రుణాన్ని నవీకరించండి
@@ -66,23 +66,23 @@
తిరిగి చెల్లించే వ్యూహం
చెల్లించండి
చెల్లింపు
- రద్దు
+ రద్దు
సమీక్ష
బదిలీ చేయుట
బదిలీ నుండి
బదిలీని చేస్తోంది
- జమ
+ జమ
మొత్తాన్ని నమోదు చేయండి
గమనిక తప్పనిసరి
- ఆమోదించబడింది
+ ఆమోదించబడింది
పెండింగ్
చెల్లించిన
సంతులనం
తిరస్కరించింది
వేచి
- జీతాలు చెల్లించిందన్న
- పెండింగ్లో ఉంది
- మీరు దరఖాస్తు చేయదలచిన అన్నింటిని ఎంచుకోండి
+ జీతాలు చెల్లించిందన్న
+ పెండింగ్లో ఉంది
+ మీరు దరఖాస్తు చేయదలచిన అన్నింటిని ఎంచుకోండి
ఫిల్టర్ సేవింగ్స్ ఖాతాలు
వడపోత రుణ ఖాతాలు
ఫిల్టర్ భాగస్వామ్యం ఖాతాలు
@@ -98,12 +98,12 @@
సేవింగ్స్ నుండి బదిలీ
రుణ ఆరోపణలు
సేవింగ్స్ ఛార్జీలు
- చురుకుగా
- మూసివేయబడింది
- ఉపసంహరించుకున్నారు
+ చురుకుగా
+ మూసివేయబడింది
+ ఉపసంహరించుకున్నారు
ఆమోదం అవసరం
- పెండింగ్
- పక్వమైనది
+ పెండింగ్
+ పక్వమైనది
ఒక ఖాతాను సృష్టించండి
మొదటి పేరు
చివరి పేరు
@@ -134,9 +134,9 @@
ఖాతా డిపాజిట్ కోసం చురుకుగా ఉండాలి
బదిలీ కోసం ఖాతా క్రియాశీలకంగా ఉండాలి
- మీకు పొదుపు ఖాతాలు లేవు
- మీకు రుణ ఖాతాలు లేవు
- మీతో సంబంధం ఉన్న ఖాతాలు ఏవీ లేవు
+ మీకు పొదుపు ఖాతాలు లేవు
+ మీకు రుణ ఖాతాలు లేవు
+ మీతో సంబంధం ఉన్న ఖాతాలు ఏవీ లేవు
మీకు సంబంధం ఉన్న లావాదేవీలు లేవు
మీకు తిరిగి చెల్లింపు షెడ్యూల్ లేదు
మరిన్ని లావాదేవీలు లేవు
@@ -227,8 +227,8 @@
తిరిగి చెల్లింపు షెడ్యూల్
లావాదేవీలు
బదిలీ
- ఆమోదం పెండింగ్లో ఉంది
- డిస్బర్స్ కోసం వేచి ఉంది
+ ఆమోదం పెండింగ్లో ఉంది
+ డిస్బర్స్ కోసం వేచి ఉంది
కొన్ని ఆబ్లిగేషన్స్ కారణంగా మూసివేయబడింది
రుణ మూసివేయబడింది
గడువు తేది:
@@ -249,7 +249,7 @@
4 వారాలు
3 నెలలు
6 నెలలు
- వడపోత
+ వడపోత
ప్రారంబపు తేది
ఆఖరి తేది
ఫిల్టర్
@@ -371,7 +371,7 @@
ఒక ఇమెయిల్ను వదిలేయండి
స్థానాలను కనుగొనండి
ఉపసంహరణలు లేవు
- ఫిల్టర్లను క్లియర్ చేయండి
+ ఫిల్టర్లను క్లియర్ చేయండి
ఖాతాలను నిర్వహించండి
మరింత
సెట్టింగ్
@@ -408,7 +408,7 @@
పాస్వర్డ్ను నిర్ధారించండి
పాస్వర్డ్ సరిపోలడం లేదు.
అన్ని హక్కులూ ప్రత్యేకించుకోవడమైనది.
- లైసెన్సుల
+ లైసెన్సుల
బదిలీని పూర్తి చేయడం సాధ్యం కాదు, దయచేసి తర్వాత మళ్లీ ప్రయత్నించండి
దయచేసి వేచి ఉండండి ...
సందేశం
diff --git a/androidApp/src/main/res/values-ur/strings.xml b/androidApp/src/main/res/values-ur/strings.xml
index bc4e91a34..f6db476fb 100644
--- a/androidApp/src/main/res/values-ur/strings.xml
+++ b/androidApp/src/main/res/values-ur/strings.xml
@@ -1,5 +1,5 @@
- Mifos موبائل
+ Mifos موبائل
لاگ ان کریں
خوش آمدید %1$s
انٹرنیٹ سے منسلک نہیں
@@ -15,9 +15,9 @@
حالیہ ٹرانزیکشنز
چارجز
سوالنامہ
- ہمارے بارے میں
+ ہمارے بارے میں
اکاؤنٹ کی تفصیلات ذخیرہ کر رہے ہیں
- بچت اکاونٹ
+ بچت اکاونٹ
قرض اکاؤنٹس کی تفصیلات لوڈ کرنے میں خرابی
نامیاتی سود کی شرح
اکاؤنٹ نمبر
@@ -30,11 +30,11 @@
چھوٹے متن
کلائنٹ اکاؤنٹس
سپلیش
- بچت
- قرض
- قرض کااکاؤنٹ
- اشتراک کریں
- اکاؤنٹ کا اشتراک کریں
+ بچت
+ قرض
+ قرض کااکاؤنٹ
+ اشتراک کریں
+ اکاؤنٹ کا اشتراک کریں
کسٹمر چیز
کام کر رہے ہیں
موجودہ حالت کی تصویر
@@ -49,8 +49,8 @@
تبصرہ
متوقع معاوضہ کی تاریخ
جمع کروانے کی تازیخ
- پیش کردی گی
- ادائیگی
+ پیش کردی گی
+ ادائیگی
منتقلی کی تاریخ
قرض کے لئے درخواست کریں
قرضہ اپ ڈیٹ کریں
@@ -66,23 +66,23 @@
واپسی کی حکمت عملی
ادایگی لینے والے ہیں
ادایگی دینے والے ہیں
- منسوخ کریں
+ منسوخ کریں
منتقلی کا جائزہ لیں
کو منتقلی
سے منتقلی
منتقلی کی جا رہی ہے
- جمع
+ جمع
رقم درج کریں
تبصرہ ضروری ہے
- منظورشدہ
+ منظورشدہ
منتقلی
ادا کیا
توازن
مسترد کردیا گیا
انتظار کر رہا ہے
- اضافی رقم دی گئی
- بقایاجات
- جو چاہے منتخب کریں
+ اضافی رقم دی گئی
+ بقایاجات
+ جو چاہے منتخب کریں
بچت کے کھاتے چھانے
قرض کے کھاتے چھانے
اشتراک کے کھاتے چھانے
@@ -98,12 +98,12 @@
بچت سے منتقلی
قرض چارج
بچت کے چارجز
- فعال
- بند
- واپس لے لیا
+ فعال
+ بند
+ واپس لے لیا
منظوری کی ضرورت ہے
- منتقلی
- متوازن
+ منتقلی
+ متوازن
کھاتا کھولیں
پہلا نام
آخری نام
@@ -134,9 +134,9 @@
جمع کرنے کے لئے اکاؤنٹ فعال ہونا چاہئے
منتقلی انجام دینے کے لئے اکاؤنٹ ہونا چاہئے
- آپ کے ساتھ منسلک کوئی بچت اکاؤنٹ نہیں ہے
- آپ کے ساتھ منسلک کوئی قرض اکاؤنٹ نہیں ہے
- آپ کے ساتھ منسلک کوئی شریک اکاؤنٹ نہیں ہے
+ آپ کے ساتھ منسلک کوئی بچت اکاؤنٹ نہیں ہے
+ آپ کے ساتھ منسلک کوئی قرض اکاؤنٹ نہیں ہے
+ آپ کے ساتھ منسلک کوئی شریک اکاؤنٹ نہیں ہے
آپ کے ساتھ کسی منتقلی کا تعلق نہیں ہے
آپ کے ساتھ منسلک کوئی ادائیگی کی شیڈول نہیں ہے
مزید ٹرانزیکشنز دستیاب نہیں ہیں
@@ -227,8 +227,8 @@
ادائیگی کی شیڈول
ٹرانسمیشن
منتقلی
- ابھی منظور نہیں ہوا
- تباہی کے انتظار میں
+ ابھی منظور نہیں ہوا
+ تباہی کے انتظار میں
کچھ مکلفات کی وجہ سے بند
قرض بند
واجب الادا تاریخ:
@@ -252,7 +252,7 @@
۴ ہفتوں
۳ ماہ
٦ ماہ
- فلٹر
+ فلٹر
آغاز کی تاریخ
ختم ہونے کی تاریخ
فلٹرڈ
@@ -366,7 +366,7 @@
%1$s ،خوش آمدید
%1$.2f
%2$s %1$.2f
- %2$s %1$s
+ %2$s %1$s
%2$.2f %1$s
%2$d :%1$s
ناجایز رقم
@@ -390,7 +390,7 @@
ای میل چھوڑ دو
مقامات تلاش کریں
کوئی واپسی نہیں
- فلٹر صاف کریں
+ فلٹر صاف کریں
اکاؤنٹس کا نظم کریں
مزید
ترتیبات
@@ -427,9 +427,9 @@
پاس ورڈ کی توثیق کریں
پاس ورڈ میچ نہیں کرتا.
ورژن٪ 1 $ s
- © 2016 - %1$s Mifos نوٹیفیکیشن.
+ © 2016 - %1$s Mifos نوٹیفیکیشن.
جملہ حقوق محفوظ ہیں.
- لائسنس
+ لائسنس
منتقلی مکمل کرنے میں ناکام، براہ کرم بعد میں دوبارہ کوشش کریں
برائے مہربانی انتظار کریں…
پیغام
diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml
index 2e45841c6..0fbff8a0a 100644
--- a/androidApp/src/main/res/values/strings.xml
+++ b/androidApp/src/main/res/values/strings.xml
@@ -1,5 +1,5 @@
- Mifos Mobile
+ Mifos Mobile
Login
Welcome %1$s
Not connected to internet
@@ -15,9 +15,9 @@
Recent Transactions
Charges
Questionnaire
- About Us
+ About Us
Saving Account Details
- Savings Account
+ Savings Account
Error loading in loan accounts detail
Nominal Interest Rate
Account Number
@@ -31,11 +31,11 @@
Small Text
Client Accounts
Splash
- Saving
- Loan
- Loan Account
- Share
- Share Account
+ Saving
+ Loan
+ Loan Account
+ Share
+ Share Account
Chose a client
Working
Status Image
@@ -50,8 +50,8 @@
Remark
Expected Disbursement Date
Submission Date
- Submitted
- Disbursement
+ Submitted
+ Disbursement
Transfer Date
Apply for Loan
Update Loan
@@ -68,23 +68,23 @@
Repayment Strategy
Pay To
Pay From
- Cancel
+ Cancel
Review
Transfer To
Transfer From
Making Transfer
- Deposit
+ Deposit
Enter Amount
Remark is mandatory
- Approved
+ Approved
Pending
Paid
Balance
Rejected
Waiting
- Overpaid
- In Arrears
- Select all filters you want to apply
+ Overpaid
+ In Arrears
+ Select all filters you want to apply
Filter Savings Accounts
Filter Loan Accounts
Filter Share Accounts
@@ -100,12 +100,12 @@
Transfer from Savings
Loan Charges
Savings Charges
- Active
- Closed
- Withdrawn
+ Active
+ Closed
+ Withdrawn
Need Approval
- Pending
- Matured
+ Pending
+ Matured
Create an account
First Name
Last Name
@@ -169,9 +169,9 @@
Request ID cannot be empty
Authentication Token cannot be empty
- There is no SavingsAccount associated with you
- There is no LoanAccount associated with you
- There is no ShareAccount associated with you
+ There is no SavingsAccount associated with you
+ There is no LoanAccount associated with you
+ There is no ShareAccount associated with you
There is no transactions associated with you
There is no repayment schedule associated with you
@@ -273,8 +273,8 @@
Repayment Schedule
Transactions
Transfer
- Approval Pending
- Waiting for Disburse
+ Approval Pending
+ Waiting for Disburse
Closed because of some Obligations
Loan Closed
Due Date:
@@ -301,7 +301,7 @@
4 Weeks
3 Months
6 Months
- Filter
+ Filter
Start Date
End Date
Filtered
@@ -466,7 +466,7 @@
Hello, %1$s
%1$.2f
%1$.2f %2$s
- %1$s %2$s
+ %1$s %2$s
%1$s%2$s
%1$s %2$.2f
%1$s: %2$d
@@ -492,7 +492,7 @@
Leave an email
Find Locations
No withdrawals
- Clear Filters
+ Clear Filters
Manage Accounts
More
Settings
@@ -523,12 +523,12 @@
Confirm Password
Password does not match.
Version %1$s
- ©2016-%1$s Mifos Initiative.
+ ©2016-%1$s Mifos Initiative.
All rights reserved.
- Licenses
- Privacy Policy
- openmf.github.io/privacy_policy_mifos_mobile.html
- https://openmf.github.io/privacy_policy_mifos_mobile.html
+ Licenses
+ Privacy Policy
+ openmf.github.io/privacy_policy_mifos_mobile.html
+ https://openmf.github.io/privacy_policy_mifos_mobile.html
Unable to complete transfer, please try again later
Please Wait...
message
@@ -553,13 +553,13 @@
Details
Product
- Dividend Payout
- Withdrawal
- Interest Posting
- Fee Deduction
- Withdrawal Transfer
- Rejected Transfer
- Overdraft Fee
+ Dividend Payout
+ Withdrawal
+ Interest Posting
+ Fee Deduction
+ Withdrawal Transfer
+ Rejected Transfer
+ Overdraft Fee
Savings Account Transaction
Transaction Period
Transaction Type
@@ -568,11 +568,11 @@
Do you want to cancel registering New Account ?
Cancel Registration
- Source Code
- Official Website
- App version 1.0
- License: MPL-2.0
- An Android Application built on top of the MifosX Self-Service platform for end-user customers to view/transact on the accounts and loans they hold.
+ Source Code
+ Official Website
+ App version 1.0
+ License: MPL-2.0
+ An Android Application built on top of the MifosX Self-Service platform for end-user customers to view/transact on the accounts and loans they hold.
or
There is no application that support this action
Theme
diff --git a/androidApp/src/main/res/xml/settings_preference.xml b/androidApp/src/main/res/xml/settings_preference.xml
index 7c93988fd..77beb21c9 100644
--- a/androidApp/src/main/res/xml/settings_preference.xml
+++ b/androidApp/src/main/res/xml/settings_preference.xml
@@ -3,7 +3,7 @@
{
add("implementation", project(":core:designsystem"))
add("implementation", project(":core:ui"))
add("implementation", project(":core:data"))
+ add("implementation", project(":core:model"))
+ add("implementation", project(":core:common"))
add("implementation", libs.findLibrary("kotlinx.collections.immutable").get())
diff --git a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt
index 4614a16ab..007ce8f34 100644
--- a/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt
+++ b/build-logic/convention/src/main/kotlin/AndroidLintConventionPlugin.kt
@@ -1,3 +1,4 @@
+
import com.android.build.api.dsl.ApplicationExtension
import com.android.build.api.dsl.LibraryExtension
import com.android.build.api.dsl.Lint
@@ -29,8 +30,10 @@ private fun Lint.configure() {
xmlReport = true
checkDependencies = true
abortOnError = false
+ enable += "ComposeM2Api"
+ error += "ComposeM2Api"
// Disable this rule until we ship the libraries to some maven.
- disable += "ResourceName"
+// disable += "ResourceName"
baseline = File("lint-baseline.xml")
explainIssues = true
htmlReport = true
diff --git a/ci-prebuild.sh b/ci-prebuild.sh
index 2bcc5ce8e..a56fccd05 100644
--- a/ci-prebuild.sh
+++ b/ci-prebuild.sh
@@ -28,7 +28,7 @@ tasks=(
"spotlessApply --no-configuration-cache"
"dependencyGuardBaseline"
"detekt"
- "testDemoDebug :lint:test :lint:lint :androidApp:lintProdRelease"
+ "testDemoDebug :lint:test :lint:lint :androidApp:lintRelease"
"build"
"updateProdReleaseBadging"
)
diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml
index 915c5a1ee..88af51a88 100644
--- a/config/detekt/detekt.yml
+++ b/config/detekt/detekt.yml
@@ -146,7 +146,7 @@ complexity:
ignoreOverloaded: false
CyclomaticComplexMethod:
active: true
- threshold: 15
+ threshold: 20
ignoreSingleWhenExpression: false
ignoreSimpleWhenEntries: false
ignoreNestingFunctions: false
@@ -674,7 +674,7 @@ style:
active: false
DestructuringDeclarationWithTooManyEntries:
active: true
- maxDestructuringEntries: 3
+ maxDestructuringEntries: 4
DoubleNegativeLambda:
active: false
negativeFunctions:
diff --git a/core/common/src/main/java/org/mifos/mobile/core/common/utils/DateHelper.kt b/core/common/src/main/java/org/mifos/mobile/core/common/utils/DateHelper.kt
index 34dd3075c..2895dac1c 100755
--- a/core/common/src/main/java/org/mifos/mobile/core/common/utils/DateHelper.kt
+++ b/core/common/src/main/java/org/mifos/mobile/core/common/utils/DateHelper.kt
@@ -124,7 +124,7 @@ object DateHelper {
}
fun getDateAsLongFromString(dateStr: String?, pattern: String?): Long? {
- val sdf = SimpleDateFormat(pattern)
+ val sdf = SimpleDateFormat(pattern, Locale.getDefault())
var date: Date? = null
try {
date = sdf.parse(dateStr)
@@ -153,13 +153,13 @@ object DateHelper {
}
fun getDateAsStringFromLong(timeInMillis: Long?): String {
- val sdf = SimpleDateFormat("dd MMM yyyy")
+ val sdf = SimpleDateFormat("dd MMM yyyy", Locale.getDefault())
return sdf.format(timeInMillis?.let { Date(it) })
}
@JvmStatic
fun getDateAndTimeAsStringFromLong(timeInMillis: Long?): String {
- val sdf = SimpleDateFormat("HH:mm a dd MMM yyyy")
+ val sdf = SimpleDateFormat("HH:mm a dd MMM yyyy", Locale.getDefault())
return sdf.format(timeInMillis?.let { Date(it) })
}
}
diff --git a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosTextButton.kt b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosButton.kt
similarity index 84%
rename from core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosTextButton.kt
rename to core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosButton.kt
index 364185cf5..5dd442be3 100644
--- a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosTextButton.kt
+++ b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosButton.kt
@@ -31,15 +31,29 @@ import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
@Composable
-fun MifosTextButton(
+fun MifosButton(
@StringRes
textResId: Int,
onClick: () -> Unit,
modifier: Modifier = Modifier,
+ enabled: Boolean = true,
+ shape: Shape = ButtonDefaults. shape,
+ colors: ButtonColors = ButtonDefaults. buttonColors(),
+ elevation: ButtonElevation? = ButtonDefaults. buttonElevation(),
+ border: BorderStroke? = null,
+ contentPadding: PaddingValues = ButtonDefaults. ContentPadding,
+ interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
Button(
modifier = modifier,
onClick = onClick,
+ enabled = enabled,
+ shape = shape,
+ colors = colors,
+ elevation = elevation,
+ border = border,
+ contentPadding = contentPadding,
+ interactionSource = interactionSource,
content = {
Text(text = stringResource(id = textResId))
},
diff --git a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosOutlinedTextField.kt b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosOutlinedTextField.kt
index 3626b472a..1cc09ed86 100644
--- a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosOutlinedTextField.kt
+++ b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosOutlinedTextField.kt
@@ -14,7 +14,6 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
@@ -35,7 +34,6 @@ import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MifosOutlinedTextField(
label: Int,
diff --git a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosSearchTextField.kt b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosSearchTextField.kt
index 0dcba14ff..ab7fba6af 100644
--- a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosSearchTextField.kt
+++ b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/components/MifosSearchTextField.kt
@@ -9,16 +9,17 @@
*/
package org.mifos.mobile.core.designsystem.components
+import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Search
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.Composable
@@ -30,7 +31,6 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MifosSearchTextField(
value: TextFieldValue,
@@ -53,16 +53,18 @@ fun MifosSearchTextField(
onValueChange = onValueChange,
textStyle = MaterialTheme.typography.bodyLarge,
trailingIcon = {
- IconButton(onClick = onSearchDismiss) {
- Icon(
- imageVector = Icons.Default.Close,
- contentDescription = "Close Icon",
- tint = Color.DarkGray,
- )
+ AnimatedVisibility(visible = value.text.isNotEmpty()) {
+ IconButton(onClick = onSearchDismiss) {
+ Icon(
+ imageVector = Icons.Default.Close,
+ contentDescription = "Close Icon",
+ )
+ }
}
},
colors = TextFieldDefaults.colors().copy(
focusedContainerColor = Color.Transparent,
+ unfocusedContainerColor = Color.Transparent,
focusedIndicatorColor = Color.LightGray,
unfocusedIndicatorColor = Color.LightGray,
focusedTextColor = if (isSystemInDarkTheme()) Color.White else Color.Black,
@@ -86,11 +88,13 @@ private fun MifosSearchTextFieldPreview(
modifier: Modifier = Modifier,
) {
MifosMobileTheme {
- MifosSearchTextField(
- value = TextFieldValue("Search"),
- onValueChange = {},
- onSearchDismiss = {},
- modifier = modifier,
- )
+ Surface {
+ MifosSearchTextField(
+ value = TextFieldValue("Search"),
+ onValueChange = {},
+ onSearchDismiss = {},
+ modifier = modifier,
+ )
+ }
}
}
diff --git a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/icons/MifosIcons.kt b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/icons/MifosIcons.kt
index 7b7db2da1..78ed0e46c 100644
--- a/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/icons/MifosIcons.kt
+++ b/core/designsystem/src/main/kotlin/org/mifos/mobile/core/designsystem/icons/MifosIcons.kt
@@ -21,12 +21,18 @@ import androidx.compose.material.icons.filled.FlashOff
import androidx.compose.material.icons.filled.FlashOn
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.Menu
+import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Notifications
import androidx.compose.material.icons.filled.Search
+import androidx.compose.material.icons.filled.Visibility
+import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material.icons.filled.WifiOff
import androidx.compose.ui.graphics.vector.ImageVector
object MifosIcons {
+ val MoreVert: ImageVector = Icons.Filled.MoreVert
+ val VisibilityOff: ImageVector = Icons.Filled.VisibilityOff
+ val Visibility: ImageVector = Icons.Filled.Visibility
val Info: ImageVector = Icons.Default.Info
val ArrowDropUp: ImageVector = Icons.Default.ArrowDropUp
val ArrowDropDown: ImageVector = Icons.Default.ArrowDropDown
diff --git a/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt b/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt
new file mode 100644
index 000000000..f4dedc245
--- /dev/null
+++ b/core/ui/src/main/java/org/mifos/mobile/core/ui/utils/ColorUtils.kt
@@ -0,0 +1,24 @@
+package org.mifos.mobile.core.ui.utils
+
+import android.app.Activity
+import android.content.Context
+import android.util.TypedValue
+import androidx.annotation.AttrRes
+import androidx.annotation.ColorInt
+import androidx.core.graphics.ColorUtils
+import androidx.core.view.WindowCompat
+
+fun Activity.setStatusBarColor(@ColorInt color: Int) {
+ if (!window.decorView.isInEditMode) {
+ window.statusBarColor = color
+ WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars =
+ ColorUtils.calculateLuminance(color) > 0.5
+ }
+}
+
+@ColorInt
+fun Context.getThemeAttributeColor(@AttrRes colorAttribute: Int): Int {
+ val typedValue = TypedValue()
+ theme.resolveAttribute(colorAttribute, typedValue, true)
+ return typedValue.data
+}
\ No newline at end of file
diff --git a/feature/about/build.gradle.kts b/feature/about/build.gradle.kts
index 02a961385..71087e7cb 100644
--- a/feature/about/build.gradle.kts
+++ b/feature/about/build.gradle.kts
@@ -17,5 +17,5 @@ android {
}
dependencies {
- implementation(projects.core.model)
+
}
\ No newline at end of file
diff --git a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt
index 8aedf7907..685640604 100644
--- a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt
+++ b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsHeader.kt
@@ -28,7 +28,7 @@ import org.mifos.mobile.feature.about.R
@DevicePreviews
@Composable
-fun AboutUsHeader(
+internal fun AboutUsHeader(
modifier: Modifier = Modifier,
) {
Column(modifier) {
@@ -42,7 +42,7 @@ fun AboutUsHeader(
)
Text(
- text = stringResource(id = R.string.app_name),
+ text = stringResource(id = R.string.feature_about_app_name),
style = MaterialTheme.typography.headlineMedium,
color = MaterialTheme.colorScheme.onSurface,
textAlign = TextAlign.Center,
@@ -52,7 +52,7 @@ fun AboutUsHeader(
)
Text(
- text = stringResource(id = R.string.about_app_description),
+ text = stringResource(id = R.string.feature_about_description),
style = MaterialTheme.typography.titleSmall.copy(),
color = MaterialTheme.colorScheme.onSurface,
textAlign = TextAlign.Center,
diff --git a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt
index 40a671e43..0b7a009de 100644
--- a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt
+++ b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/AboutUsScreen.kt
@@ -30,7 +30,7 @@ import org.mifos.mobile.feature.about.R
import java.util.Calendar
@Composable
-fun AboutUsScreen(
+internal fun AboutUsScreen(
navigateToItem: (AboutUsItem) -> Unit,
modifier: Modifier = Modifier,
) {
@@ -61,38 +61,38 @@ fun AboutUsScreen(
}
}
-fun getAboutUsItem(context: Context): List {
+private fun getAboutUsItem(context: Context): List {
val currentYear = Calendar.getInstance().get(Calendar.YEAR)
- val copyrightText = context.getString(R.string.copy_right_mifos).replace("%1\$s", currentYear.toString())
+ val copyrightText = context.getString(R.string.feature_about_copyright_mifos).replace("%1\$s", currentYear.toString())
return listOf(
AboutUsItem(
- title = context.getString(R.string.app_version_text),
+ title = context.getString(R.string.feature_about_app_version),
itemId = AboutUsListItemId.APP_VERSION_TEXT,
),
AboutUsItem(
- title = context.getString(R.string.official_website),
- iconUrl = R.drawable.ic_website,
+ title = context.getString(R.string.feature_about_official_website),
+ iconUrl = R.drawable.feature_about_website,
itemId = AboutUsListItemId.OFFICE_WEBSITE,
),
AboutUsItem(
- title = context.getString(R.string.licenses),
- iconUrl = R.drawable.ic_law_icon,
+ title = context.getString(R.string.feature_about_licenses),
+ iconUrl = R.drawable.feature_about_law_icon,
itemId = AboutUsListItemId.LICENSES,
),
AboutUsItem(
- title = context.getString(R.string.privacy_policy),
- iconUrl = R.drawable.ic_privacy_policy,
+ title = context.getString(R.string.feature_about_privacy_policy),
+ iconUrl = R.drawable.feature_about_privacy_policy,
itemId = AboutUsListItemId.PRIVACY_POLICY,
),
AboutUsItem(
- title = context.getString(R.string.sources),
- iconUrl = R.drawable.ic_source_code,
+ title = context.getString(R.string.feature_about_sources),
+ iconUrl = R.drawable.feature_about_source_code,
itemId = AboutUsListItemId.SOURCE_CODE,
),
AboutUsItem(
title = copyrightText,
- subtitle = R.string.license_string_with_value,
+ subtitle = R.string.feature_about_license,
itemId = AboutUsListItemId.LICENSES_STRING_WITH_VALUE,
),
)
diff --git a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/PrivacyPolicyScreen.kt b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/PrivacyPolicyScreen.kt
index 3b5996561..77a1dcc80 100644
--- a/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/PrivacyPolicyScreen.kt
+++ b/feature/about/src/main/java/org/mifos/mobile/feature/about/ui/PrivacyPolicyScreen.kt
@@ -33,18 +33,18 @@ import org.mifos.mobile.core.ui.component.MifosProgressIndicator
import org.mifos.mobile.feature.about.R
@Composable
-fun PrivacyPolicyScreen(
+internal fun PrivacyPolicyScreen(
navigateBack: () -> Unit,
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
MifosScaffold(
- topBarTitleResId = R.string.privacy_policy,
+ topBarTitleResId = R.string.feature_about_privacy_policy,
navigateBack = navigateBack,
modifier = modifier,
content = {
WebView(
- url = context.getString(R.string.privacy_policy_host_url),
+ url = context.getString(R.string.feature_about_policy_url),
)
},
)
@@ -52,7 +52,7 @@ fun PrivacyPolicyScreen(
@SuppressLint("SetJavaScriptEnabled")
@Composable
-fun WebView(
+private fun WebView(
url: String,
modifier: Modifier = Modifier,
) {
@@ -67,6 +67,7 @@ fun WebView(
settings.domStorageEnabled = true
overScrollMode = WebView.OVER_SCROLL_NEVER
this.webViewClient = object : WebViewClient() {
+ @Deprecated("Deprecated in Java")
override fun shouldOverrideUrlLoading(
view: WebView,
url: String,
@@ -101,5 +102,5 @@ fun WebView(
}
private fun Context.checkUri(uri: String): Boolean {
- return Uri.parse(uri)?.host?.endsWith(this.getString(R.string.privacy_policy_host)) == true
+ return Uri.parse(uri)?.host?.endsWith(this.getString(R.string.feature_about_policy_host)) == true
}
diff --git a/feature/about/src/main/res/drawable/ic_law_icon.xml b/feature/about/src/main/res/drawable/feature_about_law_icon.xml
similarity index 100%
rename from feature/about/src/main/res/drawable/ic_law_icon.xml
rename to feature/about/src/main/res/drawable/feature_about_law_icon.xml
diff --git a/feature/about/src/main/res/drawable/ic_privacy_policy.xml b/feature/about/src/main/res/drawable/feature_about_privacy_policy.xml
similarity index 100%
rename from feature/about/src/main/res/drawable/ic_privacy_policy.xml
rename to feature/about/src/main/res/drawable/feature_about_privacy_policy.xml
diff --git a/feature/about/src/main/res/drawable/ic_source_code.xml b/feature/about/src/main/res/drawable/feature_about_source_code.xml
similarity index 100%
rename from feature/about/src/main/res/drawable/ic_source_code.xml
rename to feature/about/src/main/res/drawable/feature_about_source_code.xml
diff --git a/feature/about/src/main/res/drawable/ic_website.xml b/feature/about/src/main/res/drawable/feature_about_website.xml
similarity index 100%
rename from feature/about/src/main/res/drawable/ic_website.xml
rename to feature/about/src/main/res/drawable/feature_about_website.xml
diff --git a/feature/about/src/main/res/values/strings.xml b/feature/about/src/main/res/values/strings.xml
index 706d11e39..43ff0ad18 100644
--- a/feature/about/src/main/res/values/strings.xml
+++ b/feature/about/src/main/res/values/strings.xml
@@ -1,5 +1,4 @@
-
-
- Mifos Mobile
- An Android Application built on top of the MifosX Self-Service platform for end-user customers to view/transact on the accounts and loans they hold.
- ©2016-%1$s Mifos Initiative.
- App version 1.0
- Official Website
- Licenses
- Privacy Policy
- Source Code
- License: MPL-2.0
- About Us
- openmf.github.io/privacy_policy_mifos_mobile.html
- https://openmf.github.io/privacy_policy_mifos_mobile.html
+ Mifos Mobile
+ An Android Application built on top of the MifosX Self-Service platform for end-user customers to view/transact on the accounts and loans they hold.
+ ©2016-%1$s Mifos Initiative.
+ App version 1.0
+ Official Website
+ Licenses
+ Privacy Policy
+ Source Code
+ License: MPL-2.0
+ About Us
+ openmf.github.io/privacy_policy_mifos_mobile.html
+ https://openmf.github.io/privacy_policy_mifos_mobile.html
\ No newline at end of file
diff --git a/feature/account/build.gradle.kts b/feature/account/build.gradle.kts
index 5930b9c54..b0f912380 100644
--- a/feature/account/build.gradle.kts
+++ b/feature/account/build.gradle.kts
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
plugins {
alias(libs.plugins.mifos.android.feature)
alias(libs.plugins.mifos.android.library.compose)
@@ -8,15 +17,5 @@ android {
}
dependencies {
- implementation(projects.core.ui)
- implementation(projects.core.common)
- implementation(projects.core.model)
- implementation(projects.core.data)
- implementation(libs.reactivex.rxjava2.android)
- implementation(libs.reactivex.rxjava2)
- api(libs.androidx.compose.material)
- implementation("com.github.rahul-gill.mifos-ui-library:uihouse:alpha-2.1")
- testImplementation(libs.junit)
- androidTestImplementation(libs.androidx.test.ext.junit)
- androidTestImplementation(libs.espresso.core)
+ implementation(projects.libs.pullrefresh)
}
\ No newline at end of file
diff --git a/feature/account/src/main/AndroidManifest.xml b/feature/account/src/main/AndroidManifest.xml
index a5918e68a..4ee22a4fb 100644
--- a/feature/account/src/main/AndroidManifest.xml
+++ b/feature/account/src/main/AndroidManifest.xml
@@ -1,4 +1,13 @@
-
+
+
\ No newline at end of file
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/AccountsScreen.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/AccountsScreen.kt
index 4234d6ea7..9fc6f8072 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/AccountsScreen.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/AccountsScreen.kt
@@ -1,13 +1,18 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.account.screens
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.material.ExperimentalMaterialApi
-import androidx.compose.material.pullrefresh.PullRefreshIndicator
-import androidx.compose.material.pullrefresh.pullRefresh
-import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -19,6 +24,9 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.mifos.library.pullrefresh.PullRefreshIndicator
+import com.mifos.library.pullrefresh.pullRefresh
+import com.mifos.library.pullrefresh.rememberPullRefreshState
import org.mifos.mobile.core.common.Network
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.model.entity.accounts.loan.LoanAccount
@@ -31,14 +39,15 @@ import org.mifos.mobile.feature.account.R
import org.mifos.mobile.feature.account.utils.AccountState
import org.mifos.mobile.feature.account.viewmodel.AccountsViewModel
-
@Composable
-fun AccountsScreen(
- viewModel: AccountsViewModel = hiltViewModel(),
+internal fun AccountsScreen(
accountType: String,
- onItemClick: (accountType: String, accountId: Long) -> Unit
+ onItemClick: (accountType: String, accountId: Long) -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: AccountsViewModel = hiltViewModel(),
) {
val context = LocalContext.current
+
val uiState by viewModel.accountsUiState.collectAsStateWithLifecycle()
val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle()
val isSearching by viewModel.isSearching.collectAsStateWithLifecycle()
@@ -52,63 +61,63 @@ fun AccountsScreen(
AccountsScreen(
uiState = uiState,
- isSearching = isSearching,
- isFiltered = isFiltered,
- onRetry = { viewModel.loadAccounts(accountType) },
isRefreshing = isRefreshing,
+ isFiltered = isFiltered,
+ isSearching = isSearching,
onRefresh = { viewModel.refresh(accountType) },
+ onRetry = { viewModel.loadAccounts(accountType) },
+ modifier = modifier,
searchSavingsAccountList = { accountsList ->
viewModel.searchInSavingsList(
accountsList,
- searchQuery
+ searchQuery,
)
},
searchLoanAccountList = { accountsList ->
viewModel.searchInLoanList(
accountsList,
- searchQuery
+ searchQuery,
)
},
searchShareAccountList = { accountsList ->
viewModel.searchInSharesList(
accountsList,
- searchQuery
+ searchQuery,
)
},
filterSavingsAccountList = { accountsList ->
viewModel.getFilterSavingsAccountList(
accountsList = accountsList,
filterList = filterList,
- context = context
+ context = context,
)
},
- filterLoanAccountList = { accountsList ->
+ filterLoanAccountList = { accountsList ->
viewModel.getFilterLoanAccountList(
accountsList = accountsList,
filterList = filterList,
- context = context
+ context = context,
)
},
- filterShareAccountList = { accountsList ->
+ filterShareAccountList = { accountsList ->
viewModel.getFilterShareAccountList(
accountsList = accountsList,
filterList = filterList,
- context = context
+ context = context,
)
},
- onItemClick = { accType, accountId -> onItemClick.invoke(accType, accountId) },
+ onItemClick = onItemClick,
)
}
-@OptIn(ExperimentalMaterialApi::class)
@Composable
-fun AccountsScreen(
+private fun AccountsScreen(
uiState: AccountState,
- onRetry: () -> Unit,
isRefreshing: Boolean,
- onRefresh: () -> Unit,
- isSearching: Boolean,
isFiltered: Boolean,
+ isSearching: Boolean,
+ onRefresh: () -> Unit,
+ onRetry: () -> Unit,
searchSavingsAccountList: (accountsList: List) -> List,
searchLoanAccountList: (accountsList: List) -> List,
searchShareAccountList: (accountsList: List) -> List,
@@ -116,26 +125,25 @@ fun AccountsScreen(
filterLoanAccountList: (accountsList: List) -> List,
filterShareAccountList: (accountsList: List) -> List,
onItemClick: (accountType: String, accountId: Long) -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val pullRefreshState = rememberPullRefreshState(
refreshing = isRefreshing,
- onRefresh = onRefresh
+ onRefresh = onRefresh,
)
Column(
- modifier = Modifier.fillMaxSize(),
- verticalArrangement = Arrangement.Center
+ modifier = modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
) {
-
- Box(modifier = Modifier.pullRefresh(pullRefreshState))
- {
+ Box(modifier = Modifier.pullRefresh(pullRefreshState)) {
when (uiState) {
is AccountState.Error -> {
MifosErrorComponent(
isNetworkConnected = Network.isConnected(context),
isRetryEnabled = true,
- onRetry = onRetry
+ onRetry = onRetry,
)
}
@@ -146,9 +154,9 @@ fun AccountsScreen(
is AccountState.ShowSavingsAccounts -> {
if ((uiState.savingAccounts.isNullOrEmpty())) {
EmptyDataView(
- icon = R.drawable.ic_error_black_24dp,
- error = R.string.empty_savings_accounts,
- modifier = Modifier.fillMaxSize()
+ icon = R.drawable.feature_account_error_black,
+ error = R.string.feature_account_empty_savings_accounts,
+ modifier = Modifier.fillMaxSize(),
)
} else {
SavingsAccountContent(
@@ -157,7 +165,7 @@ fun AccountsScreen(
isFiltered = isFiltered,
getUpdatedSearchList = searchSavingsAccountList,
onItemClick = onItemClick,
- getUpdatedFilterList = filterSavingsAccountList
+ getUpdatedFilterList = filterSavingsAccountList,
)
}
}
@@ -165,9 +173,9 @@ fun AccountsScreen(
is AccountState.ShowLoanAccounts -> {
if ((uiState.loanAccounts.isNullOrEmpty())) {
EmptyDataView(
- icon = R.drawable.ic_error_black_24dp,
- error = R.string.empty_loan_accounts,
- modifier = Modifier.fillMaxSize()
+ icon = R.drawable.feature_account_error_black,
+ error = R.string.feature_account_empty_loan_accounts,
+ modifier = Modifier.fillMaxSize(),
)
} else {
LoanAccountContent(
@@ -176,7 +184,7 @@ fun AccountsScreen(
isFiltered = isFiltered,
getUpdatedSearchList = searchLoanAccountList,
onItemClick = onItemClick,
- getUpdatedFilterList = filterLoanAccountList
+ getUpdatedFilterList = filterLoanAccountList,
)
}
}
@@ -184,9 +192,9 @@ fun AccountsScreen(
is AccountState.ShowShareAccounts -> {
if ((uiState.shareAccounts.isNullOrEmpty())) {
EmptyDataView(
- icon = R.drawable.ic_error_black_24dp,
- error = R.string.empty_share_accounts,
- modifier = Modifier.fillMaxSize()
+ icon = R.drawable.feature_account_error_black,
+ error = R.string.feature_account_empty_share_accounts,
+ modifier = Modifier.fillMaxSize(),
)
} else {
AccountScreenShareContent(
@@ -194,7 +202,7 @@ fun AccountsScreen(
isSearching = isSearching,
isFiltered = isFiltered,
getUpdatedSearchList = searchShareAccountList,
- getUpdatedFilterList = filterShareAccountList
+ getUpdatedFilterList = filterShareAccountList,
)
}
}
@@ -203,13 +211,13 @@ fun AccountsScreen(
PullRefreshIndicator(
refreshing = isRefreshing,
state = pullRefreshState,
- modifier = Modifier.align(Alignment.TopCenter)
+ modifier = Modifier.align(Alignment.TopCenter),
)
}
}
}
-class AccountsScreenPreviewProvider : PreviewParameterProvider {
+internal class AccountsScreenPreviewProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
@@ -224,23 +232,23 @@ class AccountsScreenPreviewProvider : PreviewParameterProvider {
@Preview(showSystemUi = true)
@Composable
private fun AccountSavingsScreenPreview(
- @PreviewParameter(AccountsScreenPreviewProvider::class) accountUiState: AccountState
+ @PreviewParameter(AccountsScreenPreviewProvider::class) accountUiState: AccountState,
) {
MifosMobileTheme {
AccountsScreen(
uiState = accountUiState,
- isSearching = true,
- isFiltered = true,
- onRetry = { },
isRefreshing = true,
+ isFiltered = true,
+ isSearching = true,
onRefresh = { },
+ onRetry = { },
+ searchSavingsAccountList = { _ -> listOf() },
+ searchLoanAccountList = { _ -> listOf() },
+ searchShareAccountList = { _ -> listOf() },
filterSavingsAccountList = { _ -> listOf() },
filterLoanAccountList = { _ -> listOf() },
filterShareAccountList = { _ -> listOf() },
- searchLoanAccountList = { _ -> listOf() },
- searchSavingsAccountList = { _ -> listOf() },
- searchShareAccountList = { _ -> listOf() },
onItemClick = { _, _ -> },
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/LoanAccountContent.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/LoanAccountContent.kt
index a333988d1..e7c54d781 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/LoanAccountContent.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/LoanAccountContent.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.account.screens
import androidx.compose.foundation.clickable
@@ -22,25 +31,26 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import org.mifos.mobile.feature.account.R
-import org.mifos.mobile.feature.account.account.utils.AccountTypeItemIndicator
-import org.mifos.mobile.core.model.entity.accounts.loan.LoanAccount
+import org.mifos.mobile.core.common.Constants
import org.mifos.mobile.core.common.utils.CurrencyUtil
import org.mifos.mobile.core.common.utils.DateHelper
+import org.mifos.mobile.core.model.entity.accounts.loan.LoanAccount
+import org.mifos.mobile.feature.account.R
+import org.mifos.mobile.feature.account.account.utils.AccountTypeItemIndicator
@Composable
-fun LoanAccountContent(
- accountsList: List,
+internal fun LoanAccountContent(
isSearching: Boolean,
- getUpdatedSearchList: (accountsList: List) -> List,
isFiltered: Boolean,
+ accountsList: List,
+ getUpdatedSearchList: (accountsList: List) -> List,
getUpdatedFilterList: (accountsList: List) -> List,
onItemClick: (accountType: String, accountId: Long) -> Unit,
+ modifier: Modifier = Modifier,
) {
+ val lazyColumnState = rememberLazyListState()
- var accounts by rememberSaveable {
- mutableStateOf(accountsList)
- }
+ var accounts by rememberSaveable { mutableStateOf(accountsList) }
accounts = when {
isFiltered && isSearching -> {
@@ -60,25 +70,24 @@ fun LoanAccountContent(
}
}
- val lazyColumnState = rememberLazyListState()
-
LazyColumn(
- modifier = Modifier.fillMaxSize(),
- state = lazyColumnState
+ modifier = modifier.fillMaxSize(),
+ state = lazyColumnState,
) {
items(items = accounts) { loanAccount ->
AccountScreenLoanListItem(
loanAccount = loanAccount,
- onItemClick = onItemClick
+ onItemClick = onItemClick,
)
}
}
}
@Composable
-fun AccountScreenLoanListItem(
+private fun AccountScreenLoanListItem(
loanAccount: LoanAccount,
- onItemClick: (accountType: String, accountId: Long) -> Unit
+ onItemClick: (accountType: String, accountId: Long) -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
@@ -86,59 +95,72 @@ fun AccountScreenLoanListItem(
loanAccount.status?.active == true && loanAccount.inArrears == true -> {
Triple(
colorResource(R.color.red),
- "${stringResource(id = R.string.disbursement)} ${DateHelper.getDateAsString(loanAccount.timeline?.actualDisbursementDate)}",
- colorResource(R.color.red)
+ "${stringResource(id = R.string.feature_account_disbursement)} " +
+ DateHelper.getDateAsString(loanAccount.timeline?.actualDisbursementDate),
+ colorResource(R.color.red),
)
}
+
loanAccount.status?.active == true -> {
Triple(
colorResource(R.color.deposit_green),
- "${stringResource(id = R.string.disbursement)} ${DateHelper.getDateAsString(loanAccount.timeline?.actualDisbursementDate)}",
- colorResource(R.color.deposit_green)
+ "${stringResource(id = R.string.feature_account_disbursement)} " +
+ DateHelper.getDateAsString(loanAccount.timeline?.actualDisbursementDate),
+ colorResource(R.color.deposit_green),
)
}
+
loanAccount.status?.waitingForDisbursal == true -> {
Triple(
colorResource(R.color.blue),
- "${stringResource(id = R.string.approved)} ${DateHelper.getDateAsString(loanAccount.timeline?.approvedOnDate)}",
- null
+ "${stringResource(id = R.string.feature_account_approved)} " +
+ DateHelper.getDateAsString(loanAccount.timeline?.approvedOnDate),
+ null,
)
}
+
loanAccount.status?.pendingApproval == true -> {
Triple(
colorResource(R.color.light_yellow),
- "${stringResource(id = R.string.submitted)} ${DateHelper.getDateAsString(loanAccount.timeline?.submittedOnDate)}",
- null
+ "${stringResource(id = R.string.feature_account_submitted)} " +
+ DateHelper.getDateAsString(loanAccount.timeline?.submittedOnDate),
+ null,
)
}
+
loanAccount.status?.overpaid == true -> {
Triple(
colorResource(R.color.purple),
- "${stringResource(id = R.string.approved)} ${DateHelper.getDateAsString(loanAccount.timeline?.actualDisbursementDate)}",
- colorResource(R.color.purple)
+ "${stringResource(id = R.string.feature_account_approved)} " +
+ DateHelper.getDateAsString(loanAccount.timeline?.actualDisbursementDate),
+ colorResource(R.color.purple),
)
}
+
loanAccount.status?.closed == true -> {
Triple(
colorResource(R.color.black),
- "${stringResource(id = R.string.closed)} ${DateHelper.getDateAsString(loanAccount.timeline?.closedOnDate)}",
- null
+ "${stringResource(id = R.string.feature_account_closed)} " +
+ DateHelper.getDateAsString(loanAccount.timeline?.closedOnDate),
+ null,
)
}
+
else -> {
Triple(
colorResource(R.color.gray_dark),
- "${stringResource(id = R.string.withdrawn)} ${DateHelper.getDateAsString(loanAccount.timeline?.withdrawnOnDate)}",
- null
+ "${stringResource(id = R.string.feature_account_withdrawn)} " +
+ DateHelper.getDateAsString(loanAccount.timeline?.withdrawnOnDate),
+ null,
)
}
}
Row(
- modifier = Modifier.clickable {
- onItemClick.invoke(org.mifos.mobile.core.common.Constants.LOAN_ACCOUNTS, loanAccount.id)
+ modifier = modifier.clickable {
+ onItemClick.invoke(Constants.LOAN_ACCOUNTS, loanAccount.id)
},
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
) {
AccountTypeItemIndicator(color)
@@ -146,7 +168,7 @@ fun AccountScreenLoanListItem(
loanAccount.accountNo?.let {
Text(
text = it,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge,
)
}
@@ -154,7 +176,7 @@ fun AccountScreenLoanListItem(
Text(
text = it,
style = MaterialTheme.typography.labelLarge,
- color = colorResource(id = R.color.gray_dark)
+ color = colorResource(id = R.color.gray_dark),
)
}
@@ -174,7 +196,7 @@ fun AccountScreenLoanListItem(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(end = 16.dp),
- color = it
+ color = it,
)
}
}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/SavingsAccountContent.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/SavingsAccountContent.kt
index dd1302839..bb1168f5e 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/SavingsAccountContent.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/SavingsAccountContent.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.account.screens
import androidx.compose.foundation.clickable
@@ -22,25 +31,26 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import org.mifos.mobile.feature.account.R
-import org.mifos.mobile.feature.account.account.utils.AccountTypeItemIndicator
-import org.mifos.mobile.core.model.entity.accounts.savings.SavingAccount
+import org.mifos.mobile.core.common.Constants
import org.mifos.mobile.core.common.utils.CurrencyUtil
import org.mifos.mobile.core.common.utils.DateHelper
+import org.mifos.mobile.core.model.entity.accounts.savings.SavingAccount
+import org.mifos.mobile.feature.account.R
+import org.mifos.mobile.feature.account.account.utils.AccountTypeItemIndicator
@Composable
-fun SavingsAccountContent(
+internal fun SavingsAccountContent(
accountsList: List,
isSearching: Boolean,
- getUpdatedSearchList: (accountsList: List) -> List,
isFiltered: Boolean,
+ getUpdatedSearchList: (accountsList: List) -> List,
getUpdatedFilterList: (accountsList: List) -> List,
onItemClick: (accountType: String, accountId: Long) -> Unit,
+ modifier: Modifier = Modifier,
) {
+ val lazyColumnState = rememberLazyListState()
- var accounts by rememberSaveable {
- mutableStateOf(accountsList)
- }
+ var accounts by rememberSaveable { mutableStateOf(accountsList) }
accounts = when {
isFiltered && isSearching -> {
@@ -60,25 +70,24 @@ fun SavingsAccountContent(
}
}
- val lazyColumnState = rememberLazyListState()
-
LazyColumn(
- modifier = Modifier.fillMaxSize(),
- state = lazyColumnState
+ modifier = modifier.fillMaxSize(),
+ state = lazyColumnState,
) {
items(items = accounts) { savingAccount ->
AccountScreenSavingsListItem(
savingAccount = savingAccount,
- onItemClick = onItemClick
+ onItemClick = onItemClick,
)
}
}
}
@Composable
-fun AccountScreenSavingsListItem(
+private fun AccountScreenSavingsListItem(
savingAccount: SavingAccount,
- onItemClick: (accountType: String, accountId: Long) -> Unit
+ onItemClick: (accountType: String, accountId: Long) -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
@@ -87,44 +96,47 @@ fun AccountScreenSavingsListItem(
Triple(
colorResource(R.color.deposit_green),
DateHelper.getDateAsString(savingAccount.lastActiveTransactionDate),
- colorResource(R.color.deposit_green)
+ colorResource(R.color.deposit_green),
)
}
savingAccount.status?.approved == true -> {
Triple(
colorResource(R.color.light_green),
- "${stringResource(id = R.string.approved)} ${DateHelper.getDateAsString(savingAccount.timeLine?.approvedOnDate)}",
- null
+ "${stringResource(id = R.string.feature_account_approved)} " +
+ DateHelper.getDateAsString(savingAccount.timeLine?.approvedOnDate),
+ null,
)
}
savingAccount.status?.submittedAndPendingApproval == true -> {
Triple(
colorResource(R.color.light_yellow),
- "${stringResource(id = R.string.submitted)} ${DateHelper.getDateAsString(savingAccount.timeLine?.submittedOnDate)}",
- null
+ "${stringResource(id = R.string.feature_account_submitted)} " +
+ DateHelper.getDateAsString(savingAccount.timeLine?.submittedOnDate),
+ null,
)
}
savingAccount.status?.matured == true -> {
Triple(
colorResource(R.color.red_light),
DateHelper.getDateAsString(savingAccount.lastActiveTransactionDate),
- colorResource(R.color.red_light)
+ colorResource(R.color.red_light),
)
}
else -> {
Triple(
colorResource(R.color.light_yellow),
- "${stringResource(id = R.string.closed)} ${DateHelper.getDateAsString(savingAccount?.timeLine?.closedOnDate)}",
- null
+ "${stringResource(id = R.string.feature_account_closed)} " +
+ DateHelper.getDateAsString(savingAccount.timeLine?.closedOnDate),
+ null,
)
}
}
Row(
- modifier = Modifier.clickable {
- onItemClick.invoke(org.mifos.mobile.core.common.Constants.SAVINGS_ACCOUNTS, savingAccount.id)
+ modifier = modifier.clickable {
+ onItemClick.invoke(Constants.SAVINGS_ACCOUNTS, savingAccount.id)
},
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
) {
AccountTypeItemIndicator(color)
@@ -132,7 +144,7 @@ fun AccountScreenSavingsListItem(
savingAccount.accountNo?.let {
Text(
text = it,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge,
)
}
@@ -155,9 +167,9 @@ fun AccountScreenSavingsListItem(
numColor?.let {
val amountBalance = context.getString(
- R.string.string_and_string,
+ R.string.feature_account_string_and_string,
savingAccount.currency?.displaySymbol ?: savingAccount.currency?.code,
- CurrencyUtil.formatCurrency(context, savingAccount.accountBalance)
+ CurrencyUtil.formatCurrency(context, savingAccount.accountBalance),
)
Text(
@@ -165,7 +177,7 @@ fun AccountScreenSavingsListItem(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(end = 16.dp),
- color = it
+ color = it,
)
}
}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/ShareAccountContent.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/ShareAccountContent.kt
index 5586f19cf..8aa9712f7 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/ShareAccountContent.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/screens/ShareAccountContent.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.account.screens
import androidx.compose.foundation.layout.Arrangement
@@ -22,22 +31,22 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import org.mifos.mobile.feature.account.R
import org.mifos.mobile.core.model.entity.accounts.share.ShareAccount
+import org.mifos.mobile.feature.account.R
import org.mifos.mobile.feature.account.account.utils.AccountTypeItemIndicator
@Composable
-fun AccountScreenShareContent(
- accountsList: List,
+internal fun AccountScreenShareContent(
isSearching: Boolean,
- getUpdatedSearchList: (accountsList: List) -> List,
isFiltered: Boolean,
- getUpdatedFilterList: (accountsList: List) -> List
+ accountsList: List,
+ getUpdatedSearchList: (accountsList: List) -> List,
+ getUpdatedFilterList: (accountsList: List) -> List,
+ modifier: Modifier = Modifier,
) {
+ val lazyColumnState = rememberLazyListState()
- var accounts by rememberSaveable {
- mutableStateOf(accountsList)
- }
+ var accounts by rememberSaveable { mutableStateOf(accountsList) }
when {
isFiltered && isSearching -> {
@@ -57,47 +66,52 @@ fun AccountScreenShareContent(
}
}
- val lazyColumnState = rememberLazyListState()
-
LazyColumn(
- modifier = Modifier.fillMaxSize(),
- state = lazyColumnState
+ modifier = modifier.fillMaxSize(),
+ state = lazyColumnState,
) {
items(items = accounts) { shareAccount ->
AccountScreenShareListItem(
- shareAccount = shareAccount
+ shareAccount = shareAccount,
)
}
}
}
@Composable
-fun AccountScreenShareListItem(
- shareAccount: ShareAccount
+private fun AccountScreenShareListItem(
+ shareAccount: ShareAccount,
+ modifier: Modifier = Modifier,
) {
val (color, setSharingAccountDetail) = when {
shareAccount.status?.active == true -> {
Pair(colorResource(R.color.deposit_green), true)
}
+
shareAccount.status?.approved == true -> {
Pair(colorResource(R.color.light_green), false)
}
+
shareAccount.status?.submittedAndPendingApproval == true -> {
Pair(colorResource(R.color.light_yellow), false)
}
+
else -> {
Pair(colorResource(R.color.light_blue), false)
}
}
- Row(verticalAlignment = Alignment.CenterVertically) {
+ Row(
+ modifier = modifier,
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
AccountTypeItemIndicator(color)
Column(modifier = Modifier.padding(all = 12.dp)) {
shareAccount.accountNo?.let {
Text(
text = it,
- style = MaterialTheme.typography.bodyLarge
+ style = MaterialTheme.typography.bodyLarge,
)
}
@@ -111,11 +125,11 @@ fun AccountScreenShareListItem(
Row(
modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween
+ horizontalArrangement = Arrangement.SpaceBetween,
) {
Row {
Text(
- text = stringResource(id = R.string.pending),
+ text = stringResource(id = R.string.feature_account_pending),
style = MaterialTheme.typography.labelLarge,
color = colorResource(id = R.color.gray_dark),
)
@@ -130,7 +144,7 @@ fun AccountScreenShareListItem(
if (setSharingAccountDetail) {
Row {
Text(
- text = stringResource(id = R.string.approved),
+ text = stringResource(id = R.string.feature_account_approved),
style = MaterialTheme.typography.labelLarge,
color = colorResource(id = R.color.gray_dark),
)
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountTypeItemIndicator.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountTypeItemIndicator.kt
index dc398e899..cc7e808ac 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountTypeItemIndicator.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountTypeItemIndicator.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.account.utils
import androidx.compose.foundation.Canvas
@@ -13,14 +22,18 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
@Composable
-fun AccountTypeItemIndicator(color: Color) {
+internal fun AccountTypeItemIndicator(
+ color: Color,
+ modifier: Modifier = Modifier,
+) {
Box(
- modifier = Modifier.background(Color.Transparent)
+ modifier = modifier
+ .background(Color.Transparent),
) {
Canvas(
modifier = Modifier
.height(60.dp)
- .width(5.dp)
+ .width(5.dp),
) {
val radius = 10.dp.toPx()
val path = androidx.compose.ui.graphics.Path().apply {
@@ -29,29 +42,29 @@ fun AccountTypeItemIndicator(color: Color) {
arcTo(
rect = androidx.compose.ui.geometry.Rect(
offset = Offset(size.width - radius, 0f),
- size = Size(radius, radius)
+ size = Size(radius, radius),
),
startAngleDegrees = -90f,
sweepAngleDegrees = 90f,
- forceMoveTo = false
+ forceMoveTo = false,
)
lineTo(size.width, size.height - radius)
arcTo(
rect = androidx.compose.ui.geometry.Rect(
offset = Offset(size.width - radius, size.height - radius),
- size = Size(radius, radius)
+ size = Size(radius, radius),
),
startAngleDegrees = 0f,
sweepAngleDegrees = 90f,
- forceMoveTo = false
+ forceMoveTo = false,
)
lineTo(0f, size.height)
close()
}
drawPath(
path = path,
- color = color
+ color = color,
)
}
}
-}
\ No newline at end of file
+}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountsFilterUtil.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountsFilterUtil.kt
index bc40f2f96..76f6bbdb8 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountsFilterUtil.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/account/utils/AccountsFilterUtil.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.account.utils
import android.content.Context
@@ -6,27 +15,27 @@ import org.mifos.mobile.feature.account.R
data class AccountsFilterUtil(
var activeString: String? = null,
var approvedString: String? = null,
- var approvalPendingString : String? = null,
- var maturedString : String? = null,
- var waitingForDisburseString : String? = null,
+ var approvalPendingString: String? = null,
+ var maturedString: String? = null,
+ var waitingForDisburseString: String? = null,
var overpaidString: String? = null,
- var closedString : String? = null,
- var withdrawnString : String? = null,
- var inArrearsString : String? = null
+ var closedString: String? = null,
+ var withdrawnString: String? = null,
+ var inArrearsString: String? = null,
) {
companion object {
fun getFilterStrings(context: Context): AccountsFilterUtil {
return AccountsFilterUtil(
- activeString = context.getString(R.string.active),
- approvedString = context.getString(R.string.approved),
- approvalPendingString = context.getString(R.string.approval_pending),
- maturedString = context.getString(R.string.matured),
- waitingForDisburseString = context.getString(R.string.waiting_for_disburse),
- overpaidString = context.getString(R.string.overpaid),
- closedString = context.getString(R.string.closed),
- withdrawnString = context.getString(R.string.withdrawn),
- inArrearsString = context.getString(R.string.in_arrears),
+ activeString = context.getString(R.string.feature_account_active),
+ approvedString = context.getString(R.string.feature_account_approved),
+ approvalPendingString = context.getString(R.string.feature_account_approval_pending),
+ maturedString = context.getString(R.string.feature_account_matured),
+ waitingForDisburseString = context.getString(R.string.feature_account_disburse),
+ overpaidString = context.getString(R.string.feature_account_overpaid),
+ closedString = context.getString(R.string.feature_account_closed),
+ withdrawnString = context.getString(R.string.feature_account_withdrawn),
+ inArrearsString = context.getString(R.string.feature_account_in_arrears),
)
}
}
-}
\ No newline at end of file
+}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/screens/ClientAccountsScreen.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/clientAccount/screens/ClientAccountsScreen.kt
similarity index 70%
rename from feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/screens/ClientAccountsScreen.kt
rename to feature/account/src/main/java/org/mifos/mobile/feature/account/clientAccount/screens/ClientAccountsScreen.kt
index 737a46c6f..72ab7d77b 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/screens/ClientAccountsScreen.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/clientAccount/screens/ClientAccountsScreen.kt
@@ -1,4 +1,13 @@
-package org.mifos.mobile.feature.account.client_account.screens
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.account.clientAccount.screens
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.isSystemInDarkTheme
@@ -18,7 +27,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.mifos.mobile.core.common.Constants
@@ -29,24 +37,28 @@ import org.mifos.mobile.core.designsystem.icons.MifosIcons
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.model.entity.CheckboxStatus
import org.mifos.mobile.core.model.enums.AccountType
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.account.R
import org.mifos.mobile.feature.account.account.screens.AccountsScreen
-import org.mifos.mobile.feature.account.client_account.utils.ClientAccountFilterDialog
-import org.mifos.mobile.feature.account.client_account.utils.ClientAccountsScreenTopBar
+import org.mifos.mobile.feature.account.clientAccount.utils.ClientAccountFilterDialog
+import org.mifos.mobile.feature.account.clientAccount.utils.ClientAccountsScreenTopBar
import org.mifos.mobile.feature.account.viewmodel.AccountsViewModel
@Composable
-fun ClientAccountsScreen(
- viewModel: AccountsViewModel = hiltViewModel(),
- navigateBack: () -> Unit?,
+internal fun ClientAccountsScreen(
+ navigateBack: () -> Unit,
navigateToLoanApplicationScreen: () -> Unit,
navigateToSavingsApplicationScreen: () -> Unit,
- onItemClick: (accountType: AccountType, accountId: Long) -> Unit
+ onItemClick: (accountType: AccountType, accountId: Long) -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: AccountsViewModel = hiltViewModel(),
) {
val context = LocalContext.current
+
var isDialogActive by rememberSaveable { mutableStateOf(false) }
- val accountType by viewModel.accountType.collectAsStateWithLifecycle()
var currentPage by rememberSaveable { mutableIntStateOf(0) }
+
+ val accountType by viewModel.accountType.collectAsStateWithLifecycle()
val filterList by viewModel.filterList.collectAsStateWithLifecycle()
LaunchedEffect(key1 = accountType) {
@@ -62,63 +74,65 @@ fun ClientAccountsScreen(
}
ClientAccountsScreen(
- navigateBack = navigateBack,
- navigateToLoanApplicationScreen = navigateToLoanApplicationScreen,
- navigateToSavingsApplicationScreen = navigateToSavingsApplicationScreen,
- onItemClick = { accountType, accountId -> onItemClick.invoke(accountType, accountId) },
- cancelFilterDialog = { isDialogActive = false },
+ currentPage = currentPage,
+ isDialogActive = isDialogActive,
+ filterList = filterList,
+ openSearch = { isDialogActive = true },
+ closeSearch = viewModel::stoppedSearching,
+ onSearchQueryChange = viewModel::updateSearchQuery,
+ pageChanged = { index -> currentPage = index },
clearFilter = {
viewModel.setFilterList(
checkBoxList = emptyList(),
currentPage = currentPage,
- context = context
+ context = context,
)
isDialogActive = false
},
+ cancelFilterDialog = { isDialogActive = false },
filterAccounts = {
viewModel.setFilterList(checkBoxList = it, currentPage = currentPage, context = context)
isDialogActive = false
},
- onSearchQueryChange = { viewModel.updateSearchQuery(query = it) },
- openSearch = { isDialogActive = true },
- closeSearch = { viewModel.stoppedSearching() },
- currentPage = currentPage,
- pageChanged = { index -> currentPage = index },
- isDialogActive = isDialogActive,
- filterList = filterList,
+ modifier = modifier,
+ navigateBack = navigateBack,
+ navigateToLoanApplicationScreen = navigateToLoanApplicationScreen,
+ navigateToSavingsApplicationScreen = navigateToSavingsApplicationScreen,
+ onItemClick = onItemClick,
)
}
@Composable
-fun ClientAccountsScreen(
- navigateBack: () -> Unit?,
- navigateToLoanApplicationScreen: () -> Unit,
- navigateToSavingsApplicationScreen: () -> Unit,
- onItemClick: (accountType: AccountType, accountId: Long) -> Unit,
- cancelFilterDialog: () -> Unit,
- clearFilter: () -> Unit,
- filterAccounts: (checkBoxList: List) -> Unit,
- onSearchQueryChange: (String) -> Unit,
- openSearch: () -> Unit,
- closeSearch: () -> Unit,
+private fun ClientAccountsScreen(
currentPage: Int,
- pageChanged: (index: Int) -> Unit,
isDialogActive: Boolean,
filterList: List,
+ openSearch: () -> Unit,
+ closeSearch: () -> Unit,
+ onSearchQueryChange: (String) -> Unit,
+ pageChanged: (index: Int) -> Unit,
+ clearFilter: () -> Unit,
+ cancelFilterDialog: () -> Unit,
+ filterAccounts: (checkBoxList: List) -> Unit,
+ navigateBack: () -> Unit,
+ navigateToLoanApplicationScreen: () -> Unit,
+ navigateToSavingsApplicationScreen: () -> Unit,
+ onItemClick: (accountType: AccountType, accountId: Long) -> Unit,
+ modifier: Modifier = Modifier,
) {
val tabs = listOf(
- stringResource(id = R.string.savings_account),
- stringResource(id = R.string.loan_account),
- stringResource(id = R.string.share_account)
+ stringResource(id = R.string.feature_account_savings_account),
+ stringResource(id = R.string.feature_account_loan_account),
+ stringResource(id = R.string.feature_account_share_account),
)
if (isDialogActive) {
ClientAccountFilterDialog(
+ title = tabs[currentPage],
filterList = filterList,
cancelDialog = { cancelFilterDialog.invoke() },
clearFilter = { clearFilter.invoke() },
updateFilterList = { list -> filterAccounts(list) },
- title = tabs[currentPage]
)
}
@@ -126,12 +140,11 @@ fun ClientAccountsScreen(
topBar = {
ClientAccountsScreenTopBar(
navigateBack = navigateBack,
- onChange = { onSearchQueryChange(it) },
- clickDialog = { openSearch.invoke() },
- closeSearch = { closeSearch.invoke() }
+ onChange = onSearchQueryChange,
+ clickDialog = openSearch,
+ closeSearch = closeSearch,
)
},
-
floatingActionButtonContent = FloatingActionButtonContent(
onClick = {
when (currentPage) {
@@ -144,119 +157,115 @@ fun ClientAccountsScreen(
Icon(
imageVector = MifosIcons.Add,
contentDescription = "Create Account",
- tint = if (isSystemInDarkTheme()) Color.Black else Color.White
+ tint = if (isSystemInDarkTheme()) Color.Black else Color.White,
)
- }
+ },
),
-
content = {
ClientAccountsTabRow(
modifier = Modifier.padding(it),
- pageChanged = { index -> pageChanged.invoke(index) },
- onItemClick = { accountType, accountId ->
- onItemClick.invoke(
- accountType,
- accountId
- )
- },
- currentPage = currentPage
+ pageChanged = pageChanged,
+ onItemClick = onItemClick,
+ currentPage = currentPage,
)
- }
+ },
+ modifier = modifier,
)
}
-
-
@OptIn(ExperimentalFoundationApi::class)
@Composable
-fun ClientAccountsTabRow(
- modifier: Modifier,
+private fun ClientAccountsTabRow(
+ currentPage: Int,
pageChanged: (index: Int) -> Unit,
onItemClick: (accountType: AccountType, accountId: Long) -> Unit,
- currentPage: Int
+ modifier: Modifier = Modifier,
) {
- var currentPage by remember { mutableIntStateOf(currentPage) }
+ var page by remember { mutableIntStateOf(currentPage) }
val pagerState = rememberPagerState(pageCount = { 3 })
+
val tabs = listOf(
- stringResource(id = R.string.savings),
- stringResource(id = R.string.loan),
- stringResource(id = R.string.share)
+ stringResource(id = R.string.feature_account_savings),
+ stringResource(id = R.string.feature_account_loan),
+ stringResource(id = R.string.feature_account_share),
)
- LaunchedEffect(key1 = currentPage) {
- pageChanged(currentPage)
- pagerState.animateScrollToPage(currentPage)
+ LaunchedEffect(key1 = page) {
+ pageChanged(page)
+ pagerState.animateScrollToPage(page)
}
- LaunchedEffect(key1 = pagerState.currentPage, pagerState.isScrollInProgress) {
- currentPage = if (!pagerState.isScrollInProgress)
+ LaunchedEffect(
+ key1 = pagerState.currentPage,
+ key2 = pagerState.isScrollInProgress,
+ ) {
+ page = if (!pagerState.isScrollInProgress) {
pagerState.currentPage
- else {
+ } else {
pagerState.targetPage
}
}
MifosTabPager(
pagerState = pagerState,
- currentPage = currentPage,
+ currentPage = page,
modifier = modifier,
tabs = tabs,
- setCurrentPage = { currentPage = it }
+ setCurrentPage = { page = it },
) {
- when (currentPage) {
+ when (page) {
0 -> AccountsScreen(
accountType = Constants.SAVINGS_ACCOUNTS,
- onItemClick = { accType, accountId ->
+ onItemClick = { _, accountId ->
onItemClick.invoke(
AccountType.SAVINGS,
- accountId
+ accountId,
)
- }
+ },
)
1 -> AccountsScreen(
accountType = Constants.LOAN_ACCOUNTS,
- onItemClick = { accType, accountId ->
+ onItemClick = { _, accountId ->
onItemClick.invoke(
AccountType.LOAN,
- accountId
+ accountId,
)
- }
+ },
)
2 -> AccountsScreen(
accountType = Constants.SHARE_ACCOUNTS,
- onItemClick = { accType, accountId ->
+ onItemClick = { _, accountId ->
onItemClick.invoke(
AccountType.SHARE,
- accountId
+ accountId,
)
- }
+ },
)
}
}
}
-
-@Preview(showSystemUi = true)
+@DevicePreviews
@Composable
-fun ClientAccountsScreenPreview() {
+private fun ClientAccountsScreenPreview() {
MifosMobileTheme {
ClientAccountsScreen(
- navigateBack = {},
- onItemClick = { accountType, accountId -> },
- cancelFilterDialog = { },
- clearFilter = { },
- filterAccounts = { },
- onSearchQueryChange = { },
- openSearch = { },
- closeSearch = { },
currentPage = 0,
- pageChanged = { index -> },
isDialogActive = false,
filterList = listOf(),
+ openSearch = { },
+ closeSearch = { },
+ onSearchQueryChange = { },
+ pageChanged = { },
+ clearFilter = { },
+ cancelFilterDialog = { },
+ filterAccounts = { },
+ navigateBack = {},
navigateToLoanApplicationScreen = {},
- navigateToSavingsApplicationScreen = {}
+ navigateToSavingsApplicationScreen = {},
+ onItemClick = { _, _ -> },
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/clientAccount/utils/ClientAccountFilterDialog.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/clientAccount/utils/ClientAccountFilterDialog.kt
new file mode 100644
index 000000000..85ff287f3
--- /dev/null
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/clientAccount/utils/ClientAccountFilterDialog.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.account.clientAccount.utils
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.CheckboxColors
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.colorResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import org.mifos.mobile.core.designsystem.components.MifosTextButton
+import org.mifos.mobile.core.model.entity.CheckboxStatus
+import org.mifos.mobile.feature.account.R
+
+@Composable
+internal fun ClientAccountFilterDialog(
+ title: String,
+ filterList: List,
+ cancelDialog: () -> Unit,
+ clearFilter: () -> Unit,
+ updateFilterList: (checkBoxList: List) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ var checkBoxList: List = filterList
+
+ AlertDialog(
+ onDismissRequest = cancelDialog,
+ modifier = modifier,
+ text = {
+ Column {
+ Text(modifier = Modifier.padding(bottom = 8.dp), text = "Filter $title")
+
+ Text(
+ modifier = Modifier.padding(bottom = 16.dp),
+ text = stringResource(R.string.feature_account_select_you_want),
+ )
+
+ ClientAccountFilterCheckBox(
+ accountStatusList = filterList,
+ updateList = { checkBoxList = it },
+ )
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ ) {
+ MifosTextButton(
+ onClick = clearFilter,
+ text = stringResource(R.string.feature_account_clear_filters),
+ )
+
+ Row {
+ MifosTextButton(
+ onClick = cancelDialog,
+ text = stringResource(R.string.feature_account_cancel),
+ )
+ MifosTextButton(
+ onClick = { updateFilterList(checkBoxList) },
+ text = stringResource(R.string.feature_account_filter),
+ )
+ }
+ }
+ }
+ },
+ confirmButton = {},
+ )
+}
+
+@Composable
+private fun ClientAccountFilterCheckBox(
+ accountStatusList: List,
+ updateList: (List) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val lazyColumnState = rememberLazyListState()
+
+ var checkBoxList by rememberSaveable { mutableStateOf(accountStatusList) }
+
+ LazyColumn(
+ state = lazyColumnState,
+ modifier = modifier,
+ ) {
+ items(checkBoxList.size) { index ->
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Start,
+ ) {
+ Checkbox(
+ checked = checkBoxList[index].isChecked,
+ onCheckedChange = {
+ val updatedList = checkBoxList.toMutableList()
+ updatedList[index] = checkBoxList[index].copy(isChecked = it)
+ checkBoxList = updatedList
+ updateList.invoke(checkBoxList)
+ },
+ colors = CheckboxColors(
+ checkedBoxColor = Color(checkBoxList[index].color),
+ uncheckedBoxColor = if (isSystemInDarkTheme()) {
+ colorResource(id = R.color.gray_light)
+ } else {
+ colorResource(id = R.color.white)
+ },
+ checkedCheckmarkColor = if (isSystemInDarkTheme()) {
+ colorResource(id = R.color.black)
+ } else {
+ colorResource(id = R.color.white)
+ },
+ uncheckedCheckmarkColor = colorResource(id = R.color.white),
+ checkedBorderColor = Color(checkBoxList[index].color),
+ uncheckedBorderColor = Color(checkBoxList[index].color),
+ disabledBorderColor = colorResource(id = R.color.gray_dark),
+ disabledIndeterminateBorderColor = colorResource(id = R.color.gray_dark),
+ disabledCheckedBoxColor = colorResource(id = R.color.black),
+ disabledUncheckedBoxColor = colorResource(id = R.color.black),
+ disabledIndeterminateBoxColor = colorResource(id = R.color.black),
+ disabledUncheckedBorderColor = colorResource(id = R.color.black),
+ ),
+ )
+ Text(
+ text = checkBoxList[index].status ?: "",
+ color = MaterialTheme.colorScheme.onSurface,
+ )
+ }
+ }
+ }
+}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountScreenTopBar.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/clientAccount/utils/ClientAccountScreenTopBar.kt
similarity index 67%
rename from feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountScreenTopBar.kt
rename to feature/account/src/main/java/org/mifos/mobile/feature/account/clientAccount/utils/ClientAccountScreenTopBar.kt
index b640d6d37..db49144a5 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountScreenTopBar.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/clientAccount/utils/ClientAccountScreenTopBar.kt
@@ -1,8 +1,16 @@
-package org.mifos.mobile.feature.account.client_account.utils
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.account.clientAccount.utils
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
-import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
@@ -11,8 +19,6 @@ 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.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -24,78 +30,82 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.unit.dp
import org.mifos.mobile.core.designsystem.components.MifosSearchTextField
import org.mifos.mobile.core.designsystem.icons.MifosIcons
@Composable
-fun ClientAccountsScreenTopBar(
- navigateBack: () -> Unit?,
+internal fun ClientAccountsScreenTopBar(
+ navigateBack: () -> Unit,
onChange: (String) -> Unit,
clickDialog: () -> Unit,
closeSearch: () -> Unit,
+ modifier: Modifier = Modifier,
) {
- var query by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
+ var query by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(TextFieldValue(""))
+ }
var isSearchActive by rememberSaveable { mutableStateOf(false) }
Row(
- Modifier.padding(top = 8.dp)
+ modifier = modifier
+ .padding(top = 8.dp)
.fillMaxWidth()
.height(50.dp),
horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
) {
-
IconButton(
- onClick = { navigateBack.invoke() },
- modifier = Modifier.size(40.dp)
+ onClick = {
+ if (isSearchActive) {
+ query = TextFieldValue("")
+ isSearchActive = false
+ closeSearch.invoke()
+ } else {
+ navigateBack.invoke()
+ }
+ },
+ modifier = Modifier.size(40.dp),
) {
Icon(
- imageVector = Icons.Filled.ArrowBack,
+ imageVector = MifosIcons.ArrowBack,
contentDescription = "Back Arrow",
- tint = if (isSystemInDarkTheme()) Color.White else Color.Black,
)
}
Box(
Modifier
.fillMaxWidth()
- .fillMaxHeight(), contentAlignment = Alignment.CenterStart
+ .fillMaxHeight(),
+ contentAlignment = Alignment.CenterStart,
) {
-
Text(
text = "Accounts",
style = MaterialTheme.typography.titleLarge,
- color = if (isSystemInDarkTheme()) Color.White else Color.Black
)
Row(
- Modifier.fillMaxWidth(),
+ modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
) {
-
IconButton(
onClick = { isSearchActive = true },
- modifier = Modifier.size(40.dp)
+ modifier = Modifier.size(40.dp),
) {
Image(
imageVector = MifosIcons.Search,
contentDescription = "Add account",
- colorFilter = ColorFilter.tint(if (isSystemInDarkTheme()) Color.White else Color.Black)
)
}
IconButton(
- onClick = { clickDialog.invoke() },
- modifier = Modifier.size(40.dp)
+ onClick = clickDialog,
+ modifier = Modifier.size(40.dp),
) {
Image(
imageVector = MifosIcons.FilterList,
contentDescription = "Add account",
- colorFilter = ColorFilter.tint(if (isSystemInDarkTheme()) Color.White else Color.Black)
)
}
}
@@ -107,7 +117,8 @@ fun ClientAccountsScreenTopBar(
query = it
onChange(it.text)
},
- modifier = Modifier.padding(end = 40.dp)
+ modifier = Modifier
+ .padding(end = 40.dp)
.height(52.dp)
.fillMaxWidth()
.background(color = MaterialTheme.colorScheme.background),
@@ -115,9 +126,9 @@ fun ClientAccountsScreenTopBar(
query = TextFieldValue("")
closeSearch.invoke()
isSearchActive = false
- }
+ },
)
}
}
}
-}
\ No newline at end of file
+}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountFilterDialog.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountFilterDialog.kt
deleted file mode 100644
index 8d3cfd034..000000000
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/client_account/utils/ClientAccountFilterDialog.kt
+++ /dev/null
@@ -1,124 +0,0 @@
-package org.mifos.mobile.feature.account.client_account.utils
-
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Checkbox
-import androidx.compose.material3.CheckboxColors
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.res.colorResource
-import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.unit.dp
-import org.mifos.mobile.feature.account.R
-import org.mifos.mobile.core.model.entity.CheckboxStatus
-
-@Composable
-fun ClientAccountFilterDialog(
- cancelDialog: () -> Unit,
- clearFilter: () -> Unit,
- updateFilterList: (checkBoxList: List) -> Unit,
- title: String,
- filterList: List
-) {
- var checkBoxList : List = filterList
-
- AlertDialog(
- onDismissRequest = { cancelDialog.invoke() },
- text = {
- Column {
- Text(modifier = Modifier.padding(bottom = 8.dp), text = "Filter $title")
- Text(modifier = Modifier.padding(bottom = 16.dp), text = stringResource(R.string.select_you_want))
-
- ClientAccountFilterCheckBox( accountStatusList = filterList ) { checkBoxList = it }
-
- Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween )
- {
- TextButton(onClick = { clearFilter.invoke() }) {
- Text(text = stringResource(R.string.clear_filters))
- }
- Row {
- TextButton(onClick = { cancelDialog.invoke() }) {
- Text(text = stringResource(R.string.cancel))
- }
- TextButton(onClick = { updateFilterList(checkBoxList) }) {
- Text(text = stringResource(R.string.filter))
- }
- }
- }
-
- }
- },
- confirmButton = {}
- )
-}
-
-@Composable
-fun ClientAccountFilterCheckBox(
- accountStatusList: List,
- updateList: (List) -> Unit
-) {
-
- var checkBoxList by rememberSaveable {
- mutableStateOf(accountStatusList)
- }
-
- val lazyColumnState = rememberLazyListState()
-
- LazyColumn(
- state = lazyColumnState
- ) {
- items( checkBoxList.size ){ index->
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Start
- )
- {
- Checkbox(
- checked = checkBoxList[index].isChecked,
- onCheckedChange = {
- val updatedList = checkBoxList.toMutableList()
- updatedList[index] = checkBoxList[index].copy(isChecked = it)
- checkBoxList = updatedList
- updateList.invoke(checkBoxList)
- },
- colors= CheckboxColors(
- checkedBoxColor = Color(checkBoxList[index].color),
- uncheckedBoxColor = if (isSystemInDarkTheme()) colorResource(id = R.color.gray_light) else colorResource(id = R.color.white) ,
- checkedCheckmarkColor = if (isSystemInDarkTheme()) colorResource(id = R.color.black) else colorResource(id = R.color.white),
- uncheckedCheckmarkColor= colorResource(id = R.color.white),
- checkedBorderColor = Color(checkBoxList[index].color),
- uncheckedBorderColor = Color(checkBoxList[index].color ),
- disabledBorderColor = colorResource(id = R.color.gray_dark),
- disabledIndeterminateBorderColor = colorResource(id = R.color.gray_dark),
- disabledCheckedBoxColor= colorResource(id = R.color.black),
- disabledUncheckedBoxColor= colorResource(id = R.color.black),
- disabledIndeterminateBoxColor= colorResource(id = R.color.black),
- disabledUncheckedBorderColor= colorResource(id = R.color.black),
- )
- )
- Text(
- text = checkBoxList[index].status ?: "",
- color = MaterialTheme.colorScheme.onSurface,
- )
- }
- }
- }
-}
-
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/navigation/AccountNavGraph.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/navigation/AccountNavGraph.kt
index 614358e26..685018d75 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/navigation/AccountNavGraph.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/navigation/AccountNavGraph.kt
@@ -1,6 +1,14 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.navigation
-import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavType
@@ -9,8 +17,7 @@ import androidx.navigation.compose.navigation
import androidx.navigation.navArgument
import org.mifos.mobile.core.common.Constants
import org.mifos.mobile.core.model.enums.AccountType
-import org.mifos.mobile.feature.account.client_account.screens.ClientAccountsScreen
-
+import org.mifos.mobile.feature.account.clientAccount.screens.ClientAccountsScreen
fun NavController.navigateToClientAccountsScreen(accountType: AccountType = AccountType.SAVINGS) {
navigate(ClientAccountsNavigation.ClientAccountsScreen.passArguments(accountType = accountType))
@@ -30,7 +37,7 @@ fun NavGraphBuilder.clientAccountsNavGraph(
navigateToAccountDetail = navigateToAccountDetail,
navigateBack = navController::popBackStack,
navigateToLoanApplicationScreen = navigateToLoanApplicationScreen,
- navigateToSavingsApplicationScreen = navigateToSavingsApplicationScreen
+ navigateToSavingsApplicationScreen = navigateToSavingsApplicationScreen,
)
}
}
@@ -39,19 +46,24 @@ fun NavGraphBuilder.clientAccountsScreenRoute(
navigateToLoanApplicationScreen: () -> Unit,
navigateToSavingsApplicationScreen: () -> Unit,
navigateToAccountDetail: (AccountType, Long) -> Unit,
- navigateBack: () -> Unit
+ navigateBack: () -> Unit,
) {
composable(
route = ClientAccountsNavigation.ClientAccountsScreen.route,
arguments = listOf(
- navArgument(name = Constants.ACCOUNT_TYPE) { type = NavType.StringType }
- )
+ navArgument(name = Constants.ACCOUNT_TYPE) { type = NavType.StringType },
+ ),
) {
ClientAccountsScreen(
navigateBack = navigateBack,
navigateToLoanApplicationScreen = navigateToLoanApplicationScreen,
navigateToSavingsApplicationScreen = navigateToSavingsApplicationScreen,
- onItemClick = { accountType, accountId -> navigateToAccountDetail(accountType, accountId) }
+ onItemClick = { accountType, accountId ->
+ navigateToAccountDetail(
+ accountType,
+ accountId,
+ )
+ },
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/navigation/AccountNavigation.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/navigation/AccountNavigation.kt
index 7d798b7b1..3756ba21f 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/navigation/AccountNavigation.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/navigation/AccountNavigation.kt
@@ -1,9 +1,16 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.navigation
import org.mifos.mobile.core.common.Constants.ACCOUNT_TYPE
import org.mifos.mobile.core.model.enums.AccountType
-import org.mifos.mobile.core.model.enums.ChargeType
-
// Constants for Routes
const val CLIENT_ACCOUNTS_NAVIGATION_ROUTE_BASE = "client_accounts_base_route"
@@ -11,9 +18,12 @@ const val CLIENT_ACCOUNTS_SCREEN_ROUTE = "client_accounts_screen_route"
// Sealed class for Navigation Routes
sealed class ClientAccountsNavigation(val route: String) {
- data object ClientAccountsBase : ClientAccountsNavigation(route = CLIENT_ACCOUNTS_NAVIGATION_ROUTE_BASE)
+ data object ClientAccountsBase :
+ ClientAccountsNavigation(route = CLIENT_ACCOUNTS_NAVIGATION_ROUTE_BASE)
- data object ClientAccountsScreen : ClientAccountsNavigation(route = "$CLIENT_ACCOUNTS_SCREEN_ROUTE/{$ACCOUNT_TYPE}") {
- fun passArguments(accountType: AccountType) = "$CLIENT_ACCOUNTS_SCREEN_ROUTE/${accountType.name}"
+ data object ClientAccountsScreen :
+ ClientAccountsNavigation(route = "$CLIENT_ACCOUNTS_SCREEN_ROUTE/{$ACCOUNT_TYPE}") {
+ fun passArguments(accountType: AccountType) =
+ "$CLIENT_ACCOUNTS_SCREEN_ROUTE/${accountType.name}"
}
-}
\ No newline at end of file
+}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/AccountState.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/AccountState.kt
index c02058197..130d5287c 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/AccountState.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/AccountState.kt
@@ -1,14 +1,22 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.utils
import org.mifos.mobile.core.model.entity.accounts.loan.LoanAccount
import org.mifos.mobile.core.model.entity.accounts.savings.SavingAccount
import org.mifos.mobile.core.model.entity.accounts.share.ShareAccount
-
sealed class AccountState {
data object Error : AccountState()
data object Loading : AccountState()
data class ShowSavingsAccounts(val savingAccounts: List?) : AccountState()
data class ShowLoanAccounts(val loanAccounts: List?) : AccountState()
data class ShowShareAccounts(val shareAccounts: List?) : AccountState()
-}
\ No newline at end of file
+}
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/StatusUtils.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/StatusUtils.kt
index 8fadffdb2..bab907e85 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/StatusUtils.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/utils/StatusUtils.kt
@@ -1,49 +1,57 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.utils
import android.content.Context
import androidx.core.content.ContextCompat
-import org.mifos.mobile.feature.account.R
import org.mifos.mobile.core.model.entity.CheckboxStatus
-import org.mifos.mobile.ui.getThemeAttributeColor
+import org.mifos.mobile.core.ui.utils.getThemeAttributeColor
+import org.mifos.mobile.feature.account.R
-/**
- * Created by dilpreet on 3/7/17.
- */
object StatusUtils {
fun getSavingsAccountStatusList(context: Context?): List {
val arrayList = ArrayList()
+
arrayList.add(
CheckboxStatus(
- context?.getString(R.string.active),
+ context?.getString(R.string.feature_account_active),
ContextCompat.getColor(context!!, R.color.deposit_green),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.approved),
+ context.getString(R.string.feature_account_approved),
ContextCompat.getColor(context, R.color.light_green),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.approval_pending),
+ context.getString(R.string.feature_account_approval_pending),
ContextCompat
.getColor(context, R.color.light_yellow),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.matured),
+ context.getString(R.string.feature_account_matured),
ContextCompat.getColor(context, R.color.red_light),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.closed),
+ context.getString(R.string.feature_account_closed),
context.getThemeAttributeColor(R.attr.colorOnSurface),
),
- )or
+ )
+
return arrayList
}
@@ -51,44 +59,44 @@ object StatusUtils {
val arrayList = ArrayList()
arrayList.add(
CheckboxStatus(
- context?.getString(R.string.in_arrears),
+ context?.getString(R.string.feature_account_in_arrears),
ContextCompat.getColor(context!!, R.color.red),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.active),
+ context.getString(R.string.feature_account_active),
ContextCompat.getColor(context, R.color.deposit_green),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.waiting_for_disburse),
+ context.getString(R.string.feature_account_disburse),
ContextCompat.getColor(context, R.color.blue),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.approval_pending),
+ context.getString(R.string.feature_account_approval_pending),
ContextCompat
.getColor(context, R.color.light_yellow),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.overpaid),
+ context.getString(R.string.feature_account_overpaid),
ContextCompat.getColor(context, R.color.purple),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.closed),
+ context.getString(R.string.feature_account_closed),
context.getThemeAttributeColor(R.attr.colorOnSurface),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.withdrawn),
+ context.getString(R.string.feature_account_withdrawn),
context.getThemeAttributeColor(R.attr.colorOnSurfaceVariant),
),
)
@@ -99,26 +107,26 @@ object StatusUtils {
val arrayList = ArrayList()
arrayList.add(
CheckboxStatus(
- context?.getString(R.string.active),
+ context?.getString(R.string.feature_account_active),
ContextCompat.getColor(context!!, R.color.deposit_green),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.approved),
+ context.getString(R.string.feature_account_approved),
ContextCompat.getColor(context, R.color.light_green),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.approval_pending),
+ context.getString(R.string.feature_account_approval_pending),
ContextCompat
.getColor(context, R.color.light_yellow),
),
)
arrayList.add(
CheckboxStatus(
- context.getString(R.string.closed),
+ context.getString(R.string.feature_account_closed),
ContextCompat.getColor(context, R.color.light_blue),
),
)
diff --git a/feature/account/src/main/java/org/mifos/mobile/feature/account/viewmodel/AccountsViewModel.kt b/feature/account/src/main/java/org/mifos/mobile/feature/account/viewmodel/AccountsViewModel.kt
index ebe9cf450..cfa307627 100644
--- a/feature/account/src/main/java/org/mifos/mobile/feature/account/viewmodel/AccountsViewModel.kt
+++ b/feature/account/src/main/java/org/mifos/mobile/feature/account/viewmodel/AccountsViewModel.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.account.viewmodel
import android.content.Context
@@ -5,7 +14,6 @@ import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
-import io.reactivex.functions.Predicate
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -34,7 +42,7 @@ import javax.inject.Inject
class AccountsViewModel @Inject constructor(
private val accountsRepositoryImp: AccountsRepository,
private val homeRepositoryImp: HomeRepository,
- savedStateHandle: SavedStateHandle
+ savedStateHandle: SavedStateHandle,
) : ViewModel() {
private val _accountsUiState = MutableStateFlow(AccountState.Loading)
@@ -55,15 +63,18 @@ class AccountsViewModel @Inject constructor(
private val _filterList = MutableStateFlow(emptyList())
val filterList: StateFlow
> = _filterList.asStateFlow()
- private val _accountTypeString = savedStateHandle.getStateFlow(key = Constants.ACCOUNT_TYPE, initialValue = AccountType.SAVINGS.name)
+ private val accountTypeString = savedStateHandle.getStateFlow(
+ key = Constants.ACCOUNT_TYPE,
+ initialValue = AccountType.SAVINGS.name,
+ )
- val accountType: StateFlow = _accountTypeString
+ val accountType: StateFlow = accountTypeString
.flatMapLatest { accountTypeString ->
flowOf(AccountType.valueOf(accountTypeString))
}.stateIn(
scope = viewModelScope,
started = SharingStarted.Eagerly,
- initialValue = null
+ initialValue = null,
)
fun refresh(accountType: String?) {
@@ -72,10 +83,12 @@ class AccountsViewModel @Inject constructor(
_isRefreshing.value = true
loadAccounts(Constants.SAVINGS_ACCOUNTS)
}
+
Constants.LOAN_ACCOUNTS -> {
_isRefreshing.value = true
loadAccounts(Constants.LOAN_ACCOUNTS)
}
+
Constants.SHARE_ACCOUNTS -> {
_isRefreshing.value = true
loadAccounts(Constants.SHARE_ACCOUNTS)
@@ -96,35 +109,36 @@ class AccountsViewModel @Inject constructor(
fun setFilterList(
checkBoxList: List,
currentPage: Int,
- context: Context
- ){
- if(checkBoxList.isEmpty()) {
+ context: Context,
+ ) {
+ if (checkBoxList.isEmpty()) {
when (currentPage) {
0 -> {
_isFiltered.update { false }
_filterList.update { StatusUtils.getSavingsAccountStatusList(context) }
}
+
1 -> {
_isFiltered.update { false }
_filterList.update { StatusUtils.getLoanAccountStatusList(context) }
}
+
2 -> {
_isFiltered.update { false }
_filterList.update { StatusUtils.getShareAccountStatusList(context) }
}
}
- }else {
+ } else {
var isChanged = false
- for( checkBox in checkBoxList )
- {
- if(checkBox.isChecked)
+ for (checkBox in checkBoxList) {
+ if (checkBox.isChecked) {
isChanged = true
+ }
}
- if(isChanged) {
+ if (isChanged) {
_isFiltered.update { true }
_filterList.update { checkBoxList }
- }
- else {
+ } else {
_isFiltered.update { false }
_filterList.update { checkBoxList }
}
@@ -134,54 +148,50 @@ class AccountsViewModel @Inject constructor(
fun getFilterLoanAccountList(
accountsList: List,
filterList: List,
- context: Context
+ context: Context,
): List {
- val uniqueAccountsMap: MutableMap = mutableMapOf()
-
- for (filter in filterList) {
- if (filter.isChecked) {
- val filteredAccounts = getFilteredLoanAccount(accountsList, filter, AccountsFilterUtil.getFilterStrings(context = context))
- for (account in filteredAccounts) {
- val identifier = getUniqueIdentifierForLoanAccount(account)
- uniqueAccountsMap[identifier] = account
- }
- }
- }
+ val accountsFilterUtil = AccountsFilterUtil.getFilterStrings(context)
- return uniqueAccountsMap.values.toList()
+ return filterList
+ .filter { it.isChecked }
+ .flatMap { filter ->
+ getFilteredLoanAccount(accountsList, filter, accountsFilterUtil)
+ }
+ .distinctBy { getUniqueIdentifierForLoanAccount(it) }
}
- fun getUniqueIdentifierForLoanAccount(account: LoanAccount): String {
+ private fun getUniqueIdentifierForLoanAccount(account: LoanAccount): String {
return account.accountNo ?: account.loanProductId.toString()
}
fun getFilterSavingsAccountList(
accountsList: List,
filterList: List,
- context: Context
+ context: Context,
): List {
+ val accountsFilterUtil = AccountsFilterUtil.getFilterStrings(context)
- val newList : MutableList = mutableListOf()
- for( filter in filterList)
- {
- if( filter.isChecked )
- newList.addAll( getFilteredSavingsAccount(accountsList,filter, AccountsFilterUtil.getFilterStrings(context = context)))
- }
- return newList
+ return filterList
+ .filter { it.isChecked }
+ .flatMap { filter ->
+ getFilteredSavingsAccount(accountsList, filter, accountsFilterUtil)
+ }
+ .distinct()
}
fun getFilterShareAccountList(
accountsList: List,
filterList: List,
- context: Context
+ context: Context,
): List {
- val newList : MutableList = mutableListOf()
- for( filter in filterList)
- {
- if(filter.isChecked)
- newList.addAll(getFilteredShareAccount(accountsList,filter, AccountsFilterUtil.getFilterStrings(context = context)))
- }
- return newList
+ val accountsFilterUtil = AccountsFilterUtil.getFilterStrings(context)
+
+ return filterList
+ .filter { it.isChecked }
+ .flatMap { filter ->
+ getFilteredShareAccount(accountsList, filter, accountsFilterUtil)
+ }
+ .distinct()
}
/**
@@ -218,16 +228,20 @@ class AccountsViewModel @Inject constructor(
_accountsUiState.value = AccountState.Error
}.collect { clientAccounts ->
when (accountType) {
- Constants.SAVINGS_ACCOUNTS -> _accountsUiState.value =
- AccountState.ShowSavingsAccounts(clientAccounts.savingsAccounts)
- Constants.LOAN_ACCOUNTS -> _accountsUiState.value =
- AccountState.ShowLoanAccounts(clientAccounts.loanAccounts)
- Constants.SHARE_ACCOUNTS -> _accountsUiState.value =
- AccountState.ShowShareAccounts(clientAccounts.shareAccounts)
+ Constants.SAVINGS_ACCOUNTS ->
+ _accountsUiState.value =
+ AccountState.ShowSavingsAccounts(clientAccounts.savingsAccounts)
+
+ Constants.LOAN_ACCOUNTS ->
+ _accountsUiState.value =
+ AccountState.ShowLoanAccounts(clientAccounts.loanAccounts)
+
+ Constants.SHARE_ACCOUNTS ->
+ _accountsUiState.value =
+ AccountState.ShowShareAccounts(clientAccounts.shareAccounts)
}
_isRefreshing.emit(false)
}
-
}
}
@@ -242,15 +256,14 @@ class AccountsViewModel @Inject constructor(
accounts: List?,
input: String?,
): List {
- return io.reactivex.Observable.fromIterable(accounts)
- .filter { (accountNo, productName) ->
- input?.lowercase(Locale.ROOT)
- ?.let { productName?.lowercase(Locale.ROOT)?.contains(it) } == true ||
- input?.lowercase(Locale.ROOT)?.let {
- accountNo?.lowercase(Locale.ROOT)
- ?.contains(it)
- } == true
- }.toList().blockingGet().filterNotNull()
+ val searchTerm = input?.lowercase(Locale.ROOT).orEmpty()
+
+ return accounts.orEmpty().filter { account ->
+ account?.let {
+ it.productName?.lowercase(Locale.ROOT)?.contains(searchTerm) == true ||
+ it.accountNo?.lowercase(Locale.ROOT)?.contains(searchTerm) == true
+ } ?: false
+ }.filterNotNull()
}
/**
@@ -264,15 +277,14 @@ class AccountsViewModel @Inject constructor(
accounts: List?,
input: String?,
): List {
- return io.reactivex.Observable.fromIterable(accounts)
- .filter { (_, _, _, accountNo, productName) ->
- input?.lowercase(Locale.ROOT)
- ?.let { productName?.lowercase(Locale.ROOT)?.contains(it) } == true ||
- input?.lowercase(Locale.ROOT)?.let {
- accountNo?.lowercase(Locale.ROOT)
- ?.contains(it)
- } == true
- }.toList().blockingGet().filterNotNull()
+ val searchTerm = input?.lowercase(Locale.ROOT).orEmpty()
+
+ return accounts.orEmpty().filter { account ->
+ account?.let {
+ it.productName?.lowercase(Locale.ROOT)?.contains(searchTerm) == true ||
+ it.accountNo?.lowercase(Locale.ROOT)?.contains(searchTerm) == true
+ } ?: false
+ }.filterNotNull()
}
/**
@@ -286,15 +298,14 @@ class AccountsViewModel @Inject constructor(
accounts: Collection?,
input: String?,
): List {
- return io.reactivex.Observable.fromIterable(accounts)
- .filter { (accountNo, _, _, _, productName) ->
- input?.lowercase(Locale.ROOT)
- ?.let { productName?.lowercase(Locale.ROOT)?.contains(it) } == true ||
- input?.lowercase(Locale.ROOT)?.let {
- accountNo?.lowercase(Locale.ROOT)
- ?.contains(it)
- } == true
- }.toList().blockingGet().filterNotNull()
+ val searchTerm = input?.lowercase(Locale.ROOT).orEmpty()
+
+ return accounts.orEmpty().filter { account ->
+ account?.let {
+ it.productName?.lowercase(Locale.ROOT)?.contains(searchTerm) == true ||
+ it.accountNo?.lowercase(Locale.ROOT)?.contains(searchTerm) == true
+ } ?: false
+ }.filterNotNull()
}
/**
@@ -304,8 +315,7 @@ class AccountsViewModel @Inject constructor(
* `checkboxStatus.isChecked()` as true.
*/
fun getCheckedStatus(statusModelList: List?): List? {
- return io.reactivex.Observable.fromIterable(statusModelList)
- .filter { (_, _, isChecked) -> isChecked }.toList().blockingGet()
+ return statusModelList?.filter { it?.isChecked == true }
}
/**
@@ -315,38 +325,23 @@ class AccountsViewModel @Inject constructor(
* @return Returns [List] of filtered [SavingAccount] according to the
* `status` provided.
*/
- fun getFilteredSavingsAccount(
+ private fun getFilteredSavingsAccount(
accounts: List?,
status: CheckboxStatus?,
- accountsFilterUtil: AccountsFilterUtil
- ): Collection {
- return io.reactivex.Observable.fromIterable(accounts)
- .filter(
- Predicate { (_, _, _, _, _, _, _, _, _, _, _, status1) ->
- if (accountsFilterUtil.activeString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.active == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.approvedString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.approved == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.approvalPendingString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.submittedAndPendingApproval == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.maturedString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.matured == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.closedString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.closed == true
- ) {
- return@Predicate true
- }
- false
- },
- ).toList().blockingGet().filterNotNull()
+ accountsFilterUtil: AccountsFilterUtil,
+ ): List {
+ return accounts.orEmpty().filter { account ->
+ when {
+ status?.status == accountsFilterUtil.activeString && account?.status?.active == true -> true
+ status?.status == accountsFilterUtil.approvedString && account?.status?.approved == true -> true
+ status?.status == accountsFilterUtil.approvalPendingString &&
+ account?.status?.submittedAndPendingApproval == true -> true
+
+ status?.status == accountsFilterUtil.maturedString && account?.status?.matured == true -> true
+ status?.status == accountsFilterUtil.closedString && account?.status?.closed == true -> true
+ else -> false
+ }
+ }.filterNotNull()
}
/**
@@ -356,46 +351,23 @@ class AccountsViewModel @Inject constructor(
* @return Returns [List] of filtered [LoanAccount] according to the
* `status` provided.
*/
- fun getFilteredLoanAccount(
+ private fun getFilteredLoanAccount(
accounts: List?,
status: CheckboxStatus?,
- accountsFilterUtil: AccountsFilterUtil
- ): Collection {
- return io.reactivex.Observable.fromIterable(accounts)
- .filter(
- Predicate { (_, _, _, _, _, _, _, _, _, _, _, status1, _, _, _, _, _, inArrears) ->
- if (accountsFilterUtil.inArrearsString?.let { status?.status?.compareTo(it) }
- == 0 && inArrears == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.activeString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.active == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.waitingForDisburseString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.waitingForDisbursal == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.approvalPendingString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.pendingApproval == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.overpaidString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.overpaid == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.closedString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.closed == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.withdrawnString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.isLoanTypeWithdrawn() == true
- ) {
- return@Predicate true
- }
- false
- },
- ).toList().blockingGet().filterNotNull()
+ accountsFilterUtil: AccountsFilterUtil,
+ ): List {
+ return accounts.orEmpty().filter { account ->
+ when (status?.status) {
+ accountsFilterUtil.inArrearsString -> account?.inArrears == true
+ accountsFilterUtil.activeString -> account?.status?.active == true
+ accountsFilterUtil.waitingForDisburseString -> account?.status?.waitingForDisbursal == true
+ accountsFilterUtil.approvalPendingString -> account?.status?.pendingApproval == true
+ accountsFilterUtil.overpaidString -> account?.status?.overpaid == true
+ accountsFilterUtil.closedString -> account?.status?.closed == true
+ accountsFilterUtil.withdrawnString -> account?.status?.isLoanTypeWithdrawn() == true
+ else -> false
+ }
+ }.filterNotNull()
}
/**
@@ -405,35 +377,19 @@ class AccountsViewModel @Inject constructor(
* @return Returns [List] of filtered [ShareAccount] according to the
* `status` provided.
*/
- fun getFilteredShareAccount(
+ private fun getFilteredShareAccount(
accounts: List?,
status: CheckboxStatus?,
- accountsFilterUtil: AccountsFilterUtil
- ): Collection {
- return io.reactivex.Observable.fromIterable(accounts)
- .filter(
- Predicate { (_, _, _, _, _, _, status1) ->
- if (accountsFilterUtil.activeString
- ?.let { status?.status?.compareTo(it) } == 0 &&
- status1?.active == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.approvedString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.approved == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.approvalPendingString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.submittedAndPendingApproval == true
- ) {
- return@Predicate true
- } else if (accountsFilterUtil.closedString
- ?.let { status?.status?.compareTo(it) } == 0 && status1?.closed == true
- ) {
- return@Predicate true
- }
- false
- },
- ).toList().blockingGet().filterNotNull()
+ accountsFilterUtil: AccountsFilterUtil,
+ ): List {
+ return accounts.orEmpty().filter { account ->
+ when (status?.status) {
+ accountsFilterUtil.activeString -> account?.status?.active == true
+ accountsFilterUtil.approvedString -> account?.status?.approved == true
+ accountsFilterUtil.approvalPendingString -> account?.status?.submittedAndPendingApproval == true
+ accountsFilterUtil.closedString -> account?.status?.closed == true
+ else -> false
+ }
+ }.filterNotNull()
}
-
-}
\ No newline at end of file
+}
diff --git a/feature/account/src/main/res/drawable/ic_error_black_24dp.xml b/feature/account/src/main/res/drawable/feature_account_error_black.xml
similarity index 51%
rename from feature/account/src/main/res/drawable/ic_error_black_24dp.xml
rename to feature/account/src/main/res/drawable/feature_account_error_black.xml
index b666c1818..3695e745c 100644
--- a/feature/account/src/main/res/drawable/ic_error_black_24dp.xml
+++ b/feature/account/src/main/res/drawable/feature_account_error_black.xml
@@ -1,3 +1,13 @@
+
+
#ff003fff
#FF0000
#1C1C1C
-
- #B2C1C8
-
-
- #ff33b5e5
-
- #33999999
-
#BB666666
-
- #ff99cc00
-
- #ffff4444
-
- #ff0099cc
-
- #ff669900
-
- #ffcc0000
-
#ffaa66cc
-
- #ffffbb33
-
- #ffff8800
-
- #ff00ddff
-
- #33CCCCCC
-
-
-
-
-
-
-
- #03A9F4
- #0288D1
- #B3E5FC
- #FF4081
- #212121
- #757575
- #FFFFFF
- #BDBDBD
- #ffffff
- @color/blue_light
- #EEEEEE
- #00000000
-
-
-
- - @color/blue_light
- - @color/green_light
- - @color/red_light
- - @color/orange_light
-
-
+ #ffff4444
+ #33999999
\ No newline at end of file
diff --git a/feature/account/src/main/res/values/strings.xml b/feature/account/src/main/res/values/strings.xml
index 412bde354..0c25741e2 100644
--- a/feature/account/src/main/res/values/strings.xml
+++ b/feature/account/src/main/res/values/strings.xml
@@ -1,37 +1,46 @@
+
- Active
- Approved
- Approval Pending
- Matured
- Waiting for Disburse
- Overpaid
- Closed
- Withdrawn
- In Arrears
- There is no SavingsAccount associated with you
- There is no LoanAccount associated with you
- There is no ShareAccount associated with you
- Disbursement
- Submitted
- %1$s %2$s
- Pending
- Savings Account
- Share Account
- Loan Account
- Saving
- Loan
- Share
- Select all filters you want to apply
- Clear Filters
- Cancel
- Filter
- Deposit
- Dividend Payout
- Withdrawal
- Interest Posting
- Fee Deduction
- Withdrawal Transfer
- Rejected Transfer
- Overdraft Fee
+ Active
+ Approved
+ Approval Pending
+ Matured
+ Waiting for Disburse
+ Overpaid
+ Closed
+ Withdrawn
+ In Arrears
+ There is no SavingsAccount associated with you
+ There is no LoanAccount associated with you
+ There is no ShareAccount associated with you
+ Disbursement
+ Submitted
+ %1$s %2$s
+ Pending
+ Savings Account
+ Share Account
+ Loan Account
+ Saving
+ Loan
+ Share
+ Select all filters you want to apply
+ Clear Filters
+ Cancel
+ Filter
+ Deposit
+ Dividend Payout
+ Withdrawal
+ Interest Posting
+ Fee Deduction
+ Withdrawal Transfer
+ Rejected Transfer
+ Overdraft Fee
\ No newline at end of file
diff --git a/feature/auth/build.gradle.kts b/feature/auth/build.gradle.kts
index 593194866..bdd37214e 100644
--- a/feature/auth/build.gradle.kts
+++ b/feature/auth/build.gradle.kts
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
plugins {
alias(libs.plugins.mifos.android.feature)
alias(libs.plugins.mifos.android.library.compose)
@@ -8,15 +17,5 @@ android {
}
dependencies {
- implementation(projects.core.ui)
- implementation(projects.core.common)
- implementation(projects.core.model)
- implementation(projects.core.data)
-
-
- implementation("com.github.ParveshSandila:CountryCodeChooser:1.0")
-
- testImplementation(libs.junit)
- androidTestImplementation(libs.androidx.test.ext.junit)
- androidTestImplementation(libs.espresso.core)
+ implementation(projects.libs.countryCodePicker)
}
\ No newline at end of file
diff --git a/feature/auth/src/androidTest/java/org/mifos/mobile/feature/auth/ExampleInstrumentedTest.kt b/feature/auth/src/androidTest/java/org/mifos/mobile/feature/auth/ExampleInstrumentedTest.kt
deleted file mode 100644
index 096a9cb2d..000000000
--- a/feature/auth/src/androidTest/java/org/mifos/mobile/feature/auth/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.mifos.mobile.feature.auth
-
-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("org.mifos.mobile.feature.auth.test", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/feature/auth/src/main/AndroidManifest.xml b/feature/auth/src/main/AndroidManifest.xml
index a5918e68a..1f9b243f0 100644
--- a/feature/auth/src/main/AndroidManifest.xml
+++ b/feature/auth/src/main/AndroidManifest.xml
@@ -1,4 +1,13 @@
+
\ No newline at end of file
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/login/screens/LoginScreen.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/login/screens/LoginScreen.kt
index 61e70cc5d..c7c4d707b 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/login/screens/LoginScreen.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/login/screens/LoginScreen.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.auth.login.screens
import android.content.Context
@@ -15,18 +24,12 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Error
-import androidx.compose.material.icons.filled.Visibility
-import androidx.compose.material.icons.filled.VisibilityOff
-import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.Divider
+import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -44,7 +47,6 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
@@ -52,105 +54,114 @@ import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import org.mifos.mobile.core.common.Network
+import org.mifos.mobile.core.designsystem.components.MifosButton
import org.mifos.mobile.core.designsystem.components.MifosOutlinedTextField
+import org.mifos.mobile.core.designsystem.components.MifosTextButton
+import org.mifos.mobile.core.designsystem.icons.MifosIcons
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.ui.component.MifosMobileIcon
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.auth.R
import org.mifos.mobile.feature.auth.login.viewmodel.LoginUiState
import org.mifos.mobile.feature.auth.login.viewmodel.LoginViewModel
@Composable
-fun LoginScreen(
- viewModel: LoginViewModel = hiltViewModel(),
+internal fun LoginScreen(
navigateToRegisterScreen: () -> Unit,
navigateToPasscodeScreen: () -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: LoginViewModel = hiltViewModel(),
) {
-
val context = LocalContext.current
val uiState by viewModel.loginUiState.collectAsStateWithLifecycle()
LoginScreen(
uiState = uiState,
- loadClient = { viewModel.loadClient() },
+ loadClient = viewModel::loadClient,
startPassCodeActivity = navigateToPasscodeScreen,
startRegisterActivity = navigateToRegisterScreen,
+ modifier = modifier,
login = { username, password ->
if (Network.isConnected(context)) {
- if (isCredentialsValid(username, password))
+ if (isCredentialsValid(username, password)) {
viewModel.login(username, password)
+ }
} else {
- Toast.makeText(context, context.getString(R.string.no_internet_connection), Toast.LENGTH_SHORT).show()
+ Toast.makeText(
+ context,
+ context.getString(R.string.no_internet_connection),
+ Toast.LENGTH_SHORT,
+ ).show()
}
- }
+ },
)
}
@Composable
-fun LoginScreen(
+private fun LoginScreen(
uiState: LoginUiState,
loadClient: () -> Unit,
startPassCodeActivity: () -> Unit,
startRegisterActivity: () -> Unit,
- login: ( username: String, password: String ) -> Unit
+ login: (username: String, password: String) -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
Box(
- modifier = Modifier
- .fillMaxSize()
+ modifier = modifier
+ .fillMaxSize(),
) {
-
LoginContent(
- login= login,
- createAccount = startRegisterActivity
+ login = login,
+ createAccount = startRegisterActivity,
)
when (uiState) {
-
LoginUiState.Initial -> Unit
is LoginUiState.Error -> {
LaunchedEffect(key1 = true) {
- Toast.makeText(context, context.getString(R.string.login_failed), Toast.LENGTH_SHORT).show()
+ showToast(context, context.getString(R.string.login_failed))
}
}
- LoginUiState.Loading -> {
- MifosProgressIndicatorOverlay()
- }
+ LoginUiState.Loading -> MifosProgressIndicatorOverlay()
- is LoginUiState.LoginSuccess -> {
- loadClient.invoke()
- }
+ is LoginUiState.LoginSuccess -> loadClient.invoke()
is LoginUiState.LoadClientSuccess -> {
startPassCodeActivity.invoke()
LaunchedEffect(key1 = true) {
- Toast.makeText(context, context.getString(R.string.toast_welcome, uiState.clientName), Toast.LENGTH_SHORT).show()
+ showToast(
+ context,
+ context.getString(R.string.toast_welcome, uiState.clientName),
+ )
}
}
}
}
}
-
@Composable
-fun LoginContent(
+@Suppress("LongMethod")
+private fun LoginContent(
login: (username: String, password: String) -> Unit,
- createAccount: () -> Unit
+ createAccount: () -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val keyboardController = LocalSoftwareKeyboardController.current
var username by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(
- TextFieldValue("")
+ TextFieldValue(""),
)
}
var password by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(
- TextFieldValue("")
+ TextFieldValue(""),
)
}
@@ -162,16 +173,18 @@ fun LoginContent(
val passwordErrorContent = validatePassword(password.text, context)
Column(
- modifier = Modifier
+ modifier = modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.pointerInput(Unit) {
- detectTapGestures(onTap = {
- keyboardController?.hide()
- })
- }
+ detectTapGestures(
+ onTap = {
+ keyboardController?.hide()
+ },
+ )
+ },
) {
- MifosMobileIcon(id = R.drawable.mifos_logo)
+ MifosMobileIcon(id = R.drawable.feature_auth_mifos_logo)
MifosOutlinedTextField(
value = username,
@@ -180,14 +193,14 @@ fun LoginContent(
usernameError = false
},
label = R.string.username,
- icon = R.drawable.ic_person_black_24dp,
+ icon = R.drawable.feature_auth_ic_person,
error = usernameError,
supportingText = usernameErrorContent,
trailingIcon = {
if (usernameError) {
- Icon(imageVector = Icons.Filled.Error, contentDescription = null)
+ Icon(imageVector = MifosIcons.Error, contentDescription = null)
}
- }
+ },
)
Spacer(modifier = Modifier.height(8.dp))
@@ -199,28 +212,35 @@ fun LoginContent(
passwordError = false
},
label = R.string.password,
- icon = R.drawable.ic_lock_black_24dp,
- visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(),
+ icon = R.drawable.feature_auth_lock,
+ visualTransformation = if (passwordVisibility) {
+ VisualTransformation.None
+ } else {
+ PasswordVisualTransformation()
+ },
trailingIcon = {
if (!passwordError) {
- val image = if (passwordVisibility)
- Icons.Filled.Visibility
- else Icons.Filled.VisibilityOff
+ val image = if (passwordVisibility) {
+ MifosIcons.Visibility
+ } else {
+ MifosIcons.VisibilityOff
+ }
IconButton(onClick = { passwordVisibility = !passwordVisibility }) {
Icon(imageVector = image, null)
}
} else {
- Icon(imageVector = Icons.Filled.Error, contentDescription = null)
+ Icon(imageVector = MifosIcons.Error, contentDescription = null)
}
},
error = passwordError,
supportingText = passwordErrorContent,
- keyboardType = KeyboardType.Password
+ keyboardType = KeyboardType.Password,
)
Spacer(modifier = Modifier.height(8.dp))
- Button(
+ MifosButton(
+ textResId = R.string.login,
onClick = {
when {
usernameErrorContent.isEmpty() && passwordErrorContent.isEmpty() -> {
@@ -247,68 +267,68 @@ fun LoginContent(
.padding(start = 16.dp, end = 16.dp, top = 4.dp),
contentPadding = PaddingValues(12.dp),
colors = ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary
- )
- ) {
- Text(text = stringResource(id = R.string.login))
- }
+ containerColor = MaterialTheme.colorScheme.primary,
+ ),
+ )
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceEvenly
+ horizontalArrangement = Arrangement.SpaceEvenly,
) {
- Divider(
+ HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp)
- .weight(1f), color = Color.Gray, thickness = 1.dp
+ .weight(1f),
+ thickness = 1.dp,
+ color = Color.Gray,
)
Text(
modifier = Modifier.padding(8.dp),
text = "or",
fontSize = 18.sp,
- color = MaterialTheme.colorScheme.onSurface
+ color = MaterialTheme.colorScheme.onSurface,
)
- Divider(
+ HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.padding(end = 16.dp)
- .weight(1f), color = Color.Gray, thickness = 1.dp
+ .weight(1f),
+ thickness = 1.dp,
+ color = Color.Gray,
)
}
- TextButton(
- onClick = {
- createAccount.invoke()
- },
+ MifosTextButton(
+ onClick = createAccount,
modifier = Modifier
.fillMaxWidth()
.align(Alignment.CenterHorizontally),
colors = ButtonDefaults.textButtonColors(
- contentColor = MaterialTheme.colorScheme.primary
- )
+ contentColor = MaterialTheme.colorScheme.primary,
+ ),
) {
Text(text = stringResource(id = R.string.create_an_account))
}
}
}
-fun isFieldEmpty(fieldText: String): Boolean {
+private fun isFieldEmpty(fieldText: String): Boolean {
return fieldText.isEmpty()
}
-fun isUsernameLengthInadequate(username: String): Boolean {
+private fun isUsernameLengthInadequate(username: String): Boolean {
return username.length < 5
}
-fun isPasswordLengthInadequate(password: String): Boolean {
+private fun isPasswordLengthInadequate(password: String): Boolean {
return password.length < 6
}
-fun usernameHasSpaces(username: String): Boolean {
+private fun usernameHasSpaces(username: String): Boolean {
return username.trim().contains(" ")
}
@@ -347,7 +367,7 @@ private fun validateUsername(username: String, context: Context): String {
usernameError = context.getString(
R.string.error_validation_blank,
context.getString(R.string.username),
- ).toString()
+ )
}
isUsernameLengthInadequate(username) -> {
@@ -355,7 +375,7 @@ private fun validateUsername(username: String, context: Context): String {
R.string.error_validation_minimum_chars,
context.getString(R.string.username),
context.resources?.getInteger(R.integer.username_minimum_length),
- ).toString()
+ )
}
usernameHasSpaces(username) -> {
@@ -363,7 +383,7 @@ private fun validateUsername(username: String, context: Context): String {
R.string.error_validation_cannot_contain_spaces,
context.getString(R.string.username),
context.getString(R.string.not_contain_username),
- ).toString()
+ )
}
}
return usernameError
@@ -376,7 +396,7 @@ private fun validatePassword(password: String, context: Context): String {
passwordError = context.getString(
R.string.error_validation_blank,
context.getString(R.string.password),
- ).toString()
+ )
}
isPasswordLengthInadequate(password) -> {
@@ -384,13 +404,13 @@ private fun validatePassword(password: String, context: Context): String {
R.string.error_validation_minimum_chars,
context.getString(R.string.password),
context.resources.getInteger(R.integer.password_minimum_length),
- ).toString()
+ )
}
}
return passwordError
}
-class LoginScreenPreviewProvider : PreviewParameterProvider {
+internal class LoginScreenPreviewProvider : PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
@@ -402,14 +422,14 @@ class LoginScreenPreviewProvider : PreviewParameterProvider {
)
}
-private fun showToast(context: Context, text : String){
+private fun showToast(context: Context, text: String) {
Toast.makeText(context, text, Toast.LENGTH_LONG).show()
}
-@Preview(showSystemUi = true)
+@DevicePreviews
@Composable
private fun LoginScreenPreview(
- @PreviewParameter(LoginScreenPreviewProvider::class) loginUiState: LoginUiState
+ @PreviewParameter(LoginScreenPreviewProvider::class) loginUiState: LoginUiState,
) {
MifosMobileTheme {
LoginScreen(
@@ -417,7 +437,7 @@ private fun LoginScreenPreview(
{},
{},
{},
- { _, _ -> }
+ { _, _ -> },
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/login/viewmodel/LoginViewModel.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/login/viewmodel/LoginViewModel.kt
index 775cd40bd..4636f0fb4 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/login/viewmodel/LoginViewModel.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/login/viewmodel/LoginViewModel.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.auth.login.viewmodel
import androidx.lifecycle.ViewModel
@@ -12,11 +21,10 @@ import org.mifos.mobile.core.data.repository.UserAuthRepository
import javax.inject.Inject
@HiltViewModel
-class LoginViewModel @Inject constructor(
+internal class LoginViewModel @Inject constructor(
private val userAuthRepositoryImp: UserAuthRepository,
- private val clientRepositoryImp: ClientRepository
-) :
- ViewModel() {
+ private val clientRepositoryImp: ClientRepository,
+) : ViewModel() {
private var _loginUiState = MutableStateFlow(LoginUiState.Initial)
val loginUiState: StateFlow get() = _loginUiState
@@ -60,10 +68,10 @@ class LoginViewModel @Inject constructor(
}
}
-sealed class LoginUiState {
+internal sealed class LoginUiState {
data object Initial : LoginUiState()
data object LoginSuccess : LoginUiState()
data object Loading : LoginUiState()
data object Error : LoginUiState()
data class LoadClientSuccess(val clientName: String?) : LoginUiState()
-}
\ No newline at end of file
+}
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavGraph.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavGraph.kt
index e35d907ff..8d3637dd2 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavGraph.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavGraph.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.auth.navigation
import androidx.navigation.NavController
@@ -9,7 +18,6 @@ import org.mifos.mobile.feature.auth.login.screens.LoginScreen
import org.mifos.mobile.feature.auth.registration.screens.RegistrationScreen
import org.mifos.mobile.feature.auth.registration.screens.RegistrationVerificationScreen
-
fun NavController.navigateToLoginScreen() {
navigate(AuthenticationNavigation.Login.route) {
popUpTo(AuthenticationNavigation.Login.route) { inclusive = true }
@@ -18,32 +26,32 @@ fun NavController.navigateToLoginScreen() {
fun NavGraphBuilder.authenticationNavGraph(
navController: NavHostController,
- navigateToPasscodeScreen: () -> Unit
+ navigateToPasscodeScreen: () -> Unit,
) {
navigation(
startDestination = AuthenticationNavigation.Login.route,
- route = AuthenticationNavigation.AuthenticationBase.route
+ route = AuthenticationNavigation.AuthenticationBase.route,
) {
loginRoute(
navigateToRegisterScreen = { navController.navigate(AuthenticationNavigation.Registration.route) },
- navigateToPasscodeScreen = navigateToPasscodeScreen
+ navigateToPasscodeScreen = navigateToPasscodeScreen,
)
registrationRoute(
navigateBack = navController::popBackStack,
- onRegistered = { navController.navigate(AuthenticationNavigation.RegistrationVerification.route) }
+ onRegistered = { navController.navigate(AuthenticationNavigation.RegistrationVerification.route) },
)
registrationVerificationRoute(
navigateBack = navController::popBackStack,
- onRegistrationVerified = navController::navigateToLoginScreen
+ onRegistrationVerified = navController::navigateToLoginScreen,
)
}
}
fun NavGraphBuilder.loginRoute(
navigateToRegisterScreen: () -> Unit,
- navigateToPasscodeScreen: () -> Unit
+ navigateToPasscodeScreen: () -> Unit,
) {
composable(route = AuthenticationNavigation.Login.route) {
LoginScreen(
@@ -53,27 +61,26 @@ fun NavGraphBuilder.loginRoute(
}
}
-
fun NavGraphBuilder.registrationRoute(
navigateBack: () -> Unit,
- onRegistered: () -> Unit
+ onRegistered: () -> Unit,
) {
- composable(route = AuthenticationNavigation.Registration.route){
+ composable(route = AuthenticationNavigation.Registration.route) {
RegistrationScreen(
onVerified = onRegistered,
- navigateBack = navigateBack
+ navigateBack = navigateBack,
)
}
}
fun NavGraphBuilder.registrationVerificationRoute(
navigateBack: () -> Unit,
- onRegistrationVerified: () -> Unit
+ onRegistrationVerified: () -> Unit,
) {
- composable(route = AuthenticationNavigation.RegistrationVerification.route){
+ composable(route = AuthenticationNavigation.RegistrationVerification.route) {
RegistrationVerificationScreen(
onVerified = onRegistrationVerified,
- navigateBack = navigateBack
+ navigateBack = navigateBack,
)
}
}
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavigationScreen.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavigationScreen.kt
index f6875ebaa..4c261bdaf 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavigationScreen.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/navigation/AuthenticationNavigationScreen.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.auth.navigation
const val AUTH_NAVIGATION_ROUTE = "auth_route"
@@ -6,8 +15,9 @@ const val REGISTRATION_SCREEN_ROUTE = "registration_screen"
const val REGISTRATION_VERIFICATION_SCREEN_ROUTE = "registration_verification_screen"
sealed class AuthenticationNavigation(val route: String) {
- data object AuthenticationBase: AuthenticationNavigation(route = AUTH_NAVIGATION_ROUTE)
- data object Login: AuthenticationNavigation(route = LOGIN_SCREEN_ROUTE)
+ data object AuthenticationBase : AuthenticationNavigation(route = AUTH_NAVIGATION_ROUTE)
+ data object Login : AuthenticationNavigation(route = LOGIN_SCREEN_ROUTE)
data object Registration : AuthenticationNavigation(route = REGISTRATION_SCREEN_ROUTE)
- data object RegistrationVerification : AuthenticationNavigation(route = REGISTRATION_VERIFICATION_SCREEN_ROUTE)
-}
\ No newline at end of file
+ data object RegistrationVerification :
+ AuthenticationNavigation(route = REGISTRATION_VERIFICATION_SCREEN_ROUTE)
+}
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/screens/RegistrationScreen.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/screens/RegistrationScreen.kt
index 833297465..55dc476f4 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/screens/RegistrationScreen.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/screens/RegistrationScreen.kt
@@ -1,10 +1,17 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.auth.registration.screens
import android.content.Context
import android.widget.Toast
-import androidx.compose.foundation.border
import androidx.compose.foundation.gestures.detectTapGestures
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -16,16 +23,14 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.verticalScroll
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Visibility
-import androidx.compose.material.icons.filled.VisibilityOff
-import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.RadioButton
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
@@ -50,97 +55,87 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.input.VisualTransformation
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
-import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getString
import androidx.core.util.PatternsCompat
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import com.owlbuddy.www.countrycodechooser.CountryCodeChooser
-import com.owlbuddy.www.countrycodechooser.utils.enums.CountryCodeType
+import com.mifos.library.countrycodepicker.CountryCodePicker
import kotlinx.coroutines.launch
+import org.mifos.mobile.core.designsystem.components.MifosButton
import org.mifos.mobile.core.designsystem.components.MifosOutlinedTextField
import org.mifos.mobile.core.designsystem.components.MifosScaffold
+import org.mifos.mobile.core.designsystem.icons.MifosIcons
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.ui.component.MifosMobileIcon
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.auth.R
import org.mifos.mobile.feature.auth.registration.utils.PasswordStrength
import org.mifos.mobile.feature.auth.registration.viewmodel.RegistrationUiState
import org.mifos.mobile.feature.auth.registration.viewmodel.RegistrationViewModel
-
-/**
- * @author pratyush
- * @since 28/12/2023
- */
@Composable
-fun RegistrationScreen(
- viewModel: RegistrationViewModel = hiltViewModel(),
+internal fun RegistrationScreen(
onVerified: () -> Unit,
- navigateBack: () -> Unit
+ navigateBack: () -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: RegistrationViewModel = hiltViewModel(),
) {
-
- val context = LocalContext.current
val uiState by viewModel.registrationUiState.collectAsStateWithLifecycle()
RegistrationScreen(
uiState = uiState,
onVerified = onVerified,
navigateBack = navigateBack,
- register = { account, username, firstname, lastname, phoneNumber, email, password, authenticationMode, countryCode ->
- viewModel.registerUser(
- accountNumber = account,
- authenticationMode = authenticationMode,
- email = email,
- firstName = firstname,
- lastName = lastname,
- mobileNumber = "$countryCode$phoneNumber",
- password = password,
- username = username
- )
- },
- progress = { updatePasswordStrengthView(it, context) }
+ register = viewModel::registerUser,
+ modifier = modifier,
)
}
-
@Composable
-fun RegistrationScreen(
+private fun RegistrationScreen(
uiState: RegistrationUiState,
onVerified: () -> Unit,
navigateBack: () -> Unit,
- register: (accountNumber: String, username: String, firstName: String, lastName: String, phoneNumber: String, email: String, password: String, authMode: String, countryCode: String) -> Unit,
- progress: (String) -> Float
+ register: (
+ accountNumber: String,
+ username: String,
+ firstName: String,
+ lastName: String,
+ phoneNumber: String,
+ email: String,
+ password: String,
+ authMode: String,
+ countryCode: String,
+ ) -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
- val snackBarHostState = remember {
- SnackbarHostState()
- }
+ val snackBarHostState = remember { SnackbarHostState() }
MifosScaffold(
topBarTitleResId = R.string.register,
navigateBack = navigateBack,
+ modifier = modifier,
snackbarHost = { SnackbarHost(hostState = snackBarHostState) },
content = { contentPadding ->
-
Box(
modifier = Modifier
.padding(contentPadding)
- .fillMaxSize()
+ .fillMaxSize(),
) {
-
RegistrationContent(
register = register,
- progress = progress,
- snackBarHostState = snackBarHostState
+ progress = {
+ updatePasswordStrengthView(it, context)
+ },
+ snackBarHostState = snackBarHostState,
)
when (uiState) {
-
RegistrationUiState.Initial -> Unit
is RegistrationUiState.Error -> {
@@ -156,13 +151,24 @@ fun RegistrationScreen(
}
}
}
- }
+ },
)
}
@Composable
-fun RegistrationContent(
- register: (accountNumber: String, username: String, firstName: String, lastName: String, phoneNumber: String, email: String, password: String, authMode: String, countryCode: String) -> Unit,
+@Suppress("LongMethod")
+private fun RegistrationContent(
+ register: (
+ accountNumber: String,
+ username: String,
+ firstName: String,
+ lastName: String,
+ phoneNumber: String,
+ email: String,
+ password: String,
+ authMode: String,
+ countryCode: String,
+ ) -> Unit,
progress: (String) -> Float,
snackBarHostState: SnackbarHostState,
) {
@@ -199,7 +205,8 @@ fun RegistrationContent(
var countryCode by rememberSaveable {
mutableStateOf("")
}
- val radioOptions = listOf(stringResource(id = R.string.rb_email), stringResource(id = R.string.rb_mobile))
+ val radioOptions =
+ listOf(stringResource(id = R.string.rb_email), stringResource(id = R.string.rb_mobile))
var authenticationMode by remember { mutableStateOf(radioOptions[0]) }
@@ -209,84 +216,91 @@ fun RegistrationContent(
val scrollState = rememberScrollState()
val coroutineScope = rememberCoroutineScope()
- LaunchedEffect(scrollState.canScrollForward){
+ LaunchedEffect(scrollState.canScrollForward) {
if (scrollState.canScrollForward) scrollState.scrollTo(scrollState.maxValue)
}
+ fun validateAllFields(): String {
+ return areFieldsValidated(
+ context = context,
+ accountNumberContent = accountNumber.text,
+ usernameContent = username.text,
+ firstNameContent = firstName.text,
+ lastNameContent = lastName.text,
+ phoneNumberContent = phoneNumber.text,
+ emailContent = email.text,
+ passwordContent = password.text,
+ confirmPasswordContent = confirmPassword.text,
+ )
+ }
+
Column(
modifier = Modifier
.fillMaxSize()
.padding(bottom = 12.dp)
.pointerInput(Unit) {
- detectTapGestures(onTap = {
- keyboardController?.hide()
- })
+ detectTapGestures(
+ onTap = {
+ keyboardController?.hide()
+ },
+ )
}
.verticalScroll(
state = scrollState,
- enabled = true
- )
+ enabled = true,
+ ),
) {
-
- MifosMobileIcon(id = R.drawable.mifos_logo)
+ MifosMobileIcon(id = R.drawable.feature_auth_mifos_logo)
MifosOutlinedTextField(
value = accountNumber,
onValueChange = { accountNumber = it },
label = R.string.account_number,
- supportingText = ""
+ supportingText = "",
)
MifosOutlinedTextField(
value = username,
onValueChange = { username = it },
label = R.string.username,
- supportingText = ""
+ supportingText = "",
)
MifosOutlinedTextField(
value = firstName,
onValueChange = { firstName = it },
label = R.string.first_name,
- supportingText = ""
+ supportingText = "",
)
MifosOutlinedTextField(
value = lastName,
onValueChange = { lastName = it },
label = R.string.last_name,
- supportingText = ""
+ supportingText = "",
)
- Row(
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Center
- ) {
- CountryCodeChooser(
- modifier = Modifier
- .padding(start = 16.dp)
- .border(
- width = 1.dp,
- shape = RoundedCornerShape(5.dp),
- color = Color.Gray
- )
- .padding(10.dp),
- defaultCountryCode = "91",
- countryCodeType = CountryCodeType.FLAG,
- onCountyCodeSelected = { code, codeWithPrefix ->
- countryCode = code
- }
- )
- MifosOutlinedTextField(
- value = phoneNumber,
- onValueChange = { phoneNumber = it },
- label = R.string.phone_number,
- supportingText = "",
- keyboardType = KeyboardType.Number
- )
- }
+
+ CountryCodePicker(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 8.dp),
+ shape = RoundedCornerShape(2.dp),
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedBorderColor = MaterialTheme.colorScheme.primary,
+ ),
+ initialPhoneNumber = phoneNumber.text,
+ onValueChange = { (code, phone), _ ->
+ phoneNumber = TextFieldValue(phone)
+ countryCode = code
+ },
+ label = { Text(stringResource(id = R.string.phone_number)) },
+ keyboardActions = KeyboardActions { keyboardController?.hide() },
+ )
+
MifosOutlinedTextField(
value = email,
onValueChange = { email = it },
label = R.string.email,
- supportingText = ""
+ supportingText = "",
)
+
MifosOutlinedTextField(
value = password,
onValueChange = {
@@ -295,20 +309,27 @@ fun RegistrationContent(
},
label = R.string.password,
supportingText = "",
- visualTransformation = if (passwordVisibility) VisualTransformation.None else PasswordVisualTransformation(),
+ visualTransformation = if (passwordVisibility) {
+ VisualTransformation.None
+ } else {
+ PasswordVisualTransformation()
+ },
trailingIcon = {
- val image = if (passwordVisibility)
- Icons.Filled.Visibility
- else Icons.Filled.VisibilityOff
+ val image = if (passwordVisibility) {
+ MifosIcons.Visibility
+ } else {
+ MifosIcons.VisibilityOff
+ }
IconButton(onClick = { passwordVisibility = !passwordVisibility }) {
Icon(imageVector = image, null)
}
},
- keyboardType = KeyboardType.Password
+ keyboardType = KeyboardType.Password,
)
if (onValueChangePassword) {
LinearProgressIndicator(
+ progress = { progressIndicator },
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp),
@@ -318,8 +339,7 @@ fun RegistrationContent(
0.75f -> Color.Green
else -> Color.Blue
},
- progress = progressIndicator,
- trackColor = Color.White
+ trackColor = Color.White,
)
}
@@ -328,72 +348,70 @@ fun RegistrationContent(
onValueChange = { confirmPassword = it },
label = R.string.confirm_password,
supportingText = "",
- visualTransformation = if (confirmPasswordVisibility) VisualTransformation.None else PasswordVisualTransformation(),
+ visualTransformation = if (confirmPasswordVisibility) {
+ VisualTransformation.None
+ } else {
+ PasswordVisualTransformation()
+ },
trailingIcon = {
- val image = if (confirmPasswordVisibility)
- Icons.Filled.Visibility
- else Icons.Filled.VisibilityOff
+ val image = if (confirmPasswordVisibility) {
+ MifosIcons.Visibility
+ } else {
+ MifosIcons.VisibilityOff
+ }
IconButton(onClick = { confirmPasswordVisibility = !confirmPasswordVisibility }) {
Icon(imageVector = image, null)
}
},
- keyboardType = KeyboardType.Password
+ keyboardType = KeyboardType.Password,
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp),
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(id = R.string.verification_mode),
modifier = Modifier.padding(end = 8.dp),
- color = MaterialTheme.colorScheme.onSurface
+ color = MaterialTheme.colorScheme.onSurface,
)
radioOptions.forEach { authMode ->
RadioButton(
selected = (authMode == authenticationMode),
- onClick = { authenticationMode = authMode }
+ onClick = { authenticationMode = authMode },
)
Text(
text = authMode,
- color = MaterialTheme.colorScheme.onSurface
+ color = MaterialTheme.colorScheme.onSurface,
)
}
}
- Button(
+ MifosButton(
+ textResId = R.string.register,
onClick = {
- val error = areFieldsValidated(
- context = context,
- accountNumberContent = accountNumber.text,
- usernameContent = username.text,
- firstNameContent = firstName.text,
- lastNameContent = lastName.text,
- phoneNumberContent = phoneNumber.text,
- emailContent = email.text,
- passwordContent = password.text,
- confirmPasswordContent = confirmPassword.text,
- )
+ val error = validateAllFields()
+
if (error == "") {
register.invoke(
- accountNumber.text,
- username.text,
- firstName.text,
- lastName.text,
- phoneNumber.text,
- email.text,
- password.text,
+ accountNumber.text,
+ username.text,
+ firstName.text,
+ lastName.text,
+ phoneNumber.text,
+ email.text,
+ password.text,
authenticationMode,
- countryCode
+ countryCode,
)
- }else {
+ } else {
coroutineScope.launch {
snackBarHostState.showSnackbar(
message = error,
actionLabel = "Ok",
- duration = SnackbarDuration.Short
+ duration = SnackbarDuration.Short,
)
}
}
@@ -405,20 +423,17 @@ fun RegistrationContent(
.padding(start = 16.dp, end = 16.dp, top = 4.dp),
contentPadding = PaddingValues(12.dp),
colors = ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary
- )
- ) {
- Text(text = stringResource(id = R.string.register))
- }
+ containerColor = MaterialTheme.colorScheme.primary,
+ ),
+ )
Spacer(modifier = Modifier.imePadding())
}
}
-fun isInputFieldBlank(fieldText: String): Boolean {
- return fieldText.trim().isEmpty()
-}
-fun isPhoneNumberValid(fieldText: String?): Boolean {
+private fun isInputFieldBlank(fieldText: String) = fieldText.trim().isEmpty()
+
+private fun isPhoneNumberValid(fieldText: String?): Boolean {
if (fieldText.isNullOrBlank()) {
return false
}
@@ -428,22 +443,17 @@ fun isPhoneNumberValid(fieldText: String?): Boolean {
return regex.matches(fieldText.trim())
}
-fun isInputLengthInadequate(fieldText: String): Boolean {
- return fieldText.trim().length < 6
-}
+private fun isInputLengthInadequate(fieldText: String) = fieldText.trim().length < 6
-fun inputHasSpaces(fieldText: String): Boolean {
- return fieldText.trim().contains(" ")
-}
+private fun inputHasSpaces(fieldText: String) = fieldText.trim().contains(" ")
-fun hasLeadingTrailingSpaces(fieldText: String): Boolean {
- return fieldText.trim().length < fieldText.length
-}
+private fun hasLeadingTrailingSpaces(fieldText: String) = fieldText.trim().length < fieldText.length
-fun isEmailInvalid(emailText: String): Boolean {
- return !PatternsCompat.EMAIL_ADDRESS.matcher(emailText.trim()).matches()
-}
-fun areFieldsValidated(
+private fun isEmailInvalid(emailText: String) =
+ !PatternsCompat.EMAIL_ADDRESS.matcher(emailText.trim()).matches()
+
+@Suppress("CyclomaticComplexMethod")
+private fun areFieldsValidated(
context: Context,
accountNumberContent: String,
usernameContent: String,
@@ -454,106 +464,80 @@ fun areFieldsValidated(
passwordContent: String,
confirmPasswordContent: String,
): String {
- return when {
- isInputFieldBlank(accountNumberContent) -> {
- val errorMessage = context.getString(
- R.string.error_validation_blank,
- context.getString(R.string.account_number)
- )
- errorMessage
- }
+ with(context) {
+ return when {
+ isInputFieldBlank(accountNumberContent) -> {
+ getString(
+ R.string.error_validation_blank,
+ context.getString(R.string.account_number),
+ )
+ }
- isInputFieldBlank(usernameContent) -> {
- val errorMessage = context.getString(
- R.string.error_validation_blank,
- context.getString(R.string.username)
- )
- errorMessage
- }
+ isInputFieldBlank(usernameContent) -> {
+ getString(R.string.error_validation_blank, context.getString(R.string.username))
+ }
- isInputLengthInadequate(usernameContent) -> {
- val errorMessage = context.getString(R.string.error_username_greater_than_six)
- errorMessage
- }
+ isInputLengthInadequate(usernameContent) -> {
+ getString(R.string.error_username_greater_than_six)
+ }
- inputHasSpaces(usernameContent) -> {
- val errorMessage = context.getString(
- R.string.error_validation_cannot_contain_spaces,
- context.getString(R.string.username),
- context.getString(R.string.not_contain_username),
- )
- errorMessage
- }
+ inputHasSpaces(usernameContent) -> {
+ getString(
+ R.string.error_validation_cannot_contain_spaces,
+ context.getString(R.string.username),
+ context.getString(R.string.not_contain_username),
+ )
+ }
- isInputFieldBlank(firstNameContent) -> {
- val errorMessage = context.getString(
- R.string.error_validation_blank,
- context.getString(R.string.first_name)
- )
- errorMessage
- }
+ isInputFieldBlank(firstNameContent) -> {
+ getString(R.string.error_validation_blank, context.getString(R.string.first_name))
+ }
- isInputFieldBlank(lastNameContent) -> {
- val errorMessage = context.getString(
- R.string.error_validation_blank,
- context.getString(R.string.last_name)
- )
- errorMessage
- }
+ isInputFieldBlank(lastNameContent) -> {
+ getString(R.string.error_validation_blank, context.getString(R.string.last_name))
+ }
- !isPhoneNumberValid(phoneNumberContent) -> {
- val errorMessage = context.getString(R.string.invalid_phn_number)
- errorMessage
- }
+ !isPhoneNumberValid(phoneNumberContent) -> {
+ getString(R.string.invalid_phn_number)
+ }
- isInputFieldBlank(emailContent) -> {
- val errorMessage = context.getString(
- R.string.error_validation_blank,
- context.getString(R.string.email)
- )
- errorMessage
- }
+ isInputFieldBlank(emailContent) -> {
+ getString(R.string.error_validation_blank, context.getString(R.string.email))
+ }
- isInputFieldBlank(passwordContent) -> {
- val errorMessage = context.getString(
- R.string.error_validation_blank,
- context.getString(R.string.password)
- )
- errorMessage
- }
+ isInputFieldBlank(passwordContent) -> {
+ getString(R.string.error_validation_blank, context.getString(R.string.password))
+ }
- hasLeadingTrailingSpaces(passwordContent) -> {
- val errorMessage = context.getString(
- R.string.error_validation_cannot_contain_leading_or_trailing_spaces,
- context.getString(R.string.password),
- )
- errorMessage
- }
+ hasLeadingTrailingSpaces(passwordContent) -> {
+ getString(
+ R.string.error_validation_cannot_contain_leading_or_trailing_spaces,
+ context.getString(R.string.password),
+ )
+ }
- isEmailInvalid(emailContent) -> {
- val errorMessage = ContextCompat.getString(context, R.string.error_invalid_email)
- errorMessage
- }
+ isEmailInvalid(emailContent) -> {
+ getString(context, R.string.error_invalid_email)
+ }
- isInputLengthInadequate(passwordContent) -> {
- val errorMessage = context.getString(
- R.string.error_validation_minimum_chars,
- context.getString(R.string.password),
- context.resources.getInteger(R.integer.password_minimum_length),
- )
- errorMessage
- }
+ isInputLengthInadequate(passwordContent) -> {
+ getString(
+ R.string.error_validation_minimum_chars,
+ context.getString(R.string.password),
+ context.resources.getInteger(R.integer.password_minimum_length),
+ )
+ }
- passwordContent != confirmPasswordContent -> {
- val errorMessage = context.getString(R.string.error_password_not_match)
- errorMessage
- }
+ passwordContent != confirmPasswordContent -> {
+ getString(R.string.error_password_not_match)
+ }
- else -> ""
+ else -> ""
+ }
}
}
-fun updatePasswordStrengthView(password: String, context: Context): Float {
+private fun updatePasswordStrengthView(password: String, context: Context): Float {
val str = PasswordStrength.calculateStrength(password)
return when (str.getText(context)) {
getString(context, R.string.password_strength_weak) -> 0.25f
@@ -563,31 +547,29 @@ fun updatePasswordStrengthView(password: String, context: Context): Float {
}
}
-class RegistrationScreenPreviewProvider : PreviewParameterProvider {
+internal class RegistrationScreenPreviewProvider : PreviewParameterProvider {
override val values: Sequence
- get() = sequenceOf(
- RegistrationUiState.Loading,
- RegistrationUiState.Error(1),
- RegistrationUiState.Success,
- RegistrationUiState.Initial,
- )
+ get() = sequenceOf(
+ RegistrationUiState.Loading,
+ RegistrationUiState.Error(1),
+ RegistrationUiState.Success,
+ RegistrationUiState.Initial,
+ )
}
-@Preview(showSystemUi = true)
+@DevicePreviews
@Composable
-
private fun RegistrationScreenPreview(
- @PreviewParameter(RegistrationScreenPreviewProvider::class) registrationUiState: RegistrationUiState
+ @PreviewParameter(RegistrationScreenPreviewProvider::class)
+ registrationUiState: RegistrationUiState,
) {
MifosMobileTheme {
RegistrationScreen(
- registrationUiState,
- {},
- {},
- { _, _, _, _, _, _, _, _, _ -> },
- { 0f }
+ uiState = registrationUiState,
+ onVerified = {},
+ navigateBack = {},
+ register = { _, _, _, _, _, _, _, _, _ -> },
)
-
}
-}
\ No newline at end of file
+}
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/screens/RegistrationVerificationScreen.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/screens/RegistrationVerificationScreen.kt
index dea2d83db..3a01cc6b3 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/screens/RegistrationVerificationScreen.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/screens/RegistrationVerificationScreen.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.auth.registration.screens
import android.widget.Toast
@@ -11,15 +20,11 @@ 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.material.icons.Icons
-import androidx.compose.material.icons.filled.Error
import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -34,26 +39,31 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import org.mifos.mobile.core.designsystem.components.MifosButton
import org.mifos.mobile.core.designsystem.components.MifosOutlinedTextField
import org.mifos.mobile.core.designsystem.components.MifosScaffold
+import org.mifos.mobile.core.designsystem.components.MifosTextButton
+import org.mifos.mobile.core.designsystem.icons.MifosIcons
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.auth.R
import org.mifos.mobile.feature.auth.registration.viewmodel.RegistrationUiState
import org.mifos.mobile.feature.auth.registration.viewmodel.RegistrationViewModel
@Composable
-fun RegistrationVerificationScreen(
+internal fun RegistrationVerificationScreen(
navigateBack: () -> Unit?,
- onVerified: () -> Unit
+ onVerified: () -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: RegistrationViewModel = hiltViewModel(),
) {
-
+ val uiState by viewModel.registrationVerificationUiState.collectAsStateWithLifecycle()
var showConfirmationDialog by remember { mutableStateOf(false) }
BackHandler(enabled = true) {
@@ -64,84 +74,88 @@ fun RegistrationVerificationScreen(
AlertDialog(
onDismissRequest = { showConfirmationDialog = false },
title = { Text(text = stringResource(R.string.dialog_cancel_registration_title)) },
- text = { Text(text = stringResource(R.string.dialog_cancel_registration_message)) },
+ text = {
+ Text(text = stringResource(R.string.dialog_cancel_registration_message))
+ },
+ modifier = modifier,
confirmButton = {
- TextButton(onClick = {
- showConfirmationDialog = false
- navigateBack.invoke()
- }) {
+ MifosTextButton(
+ onClick = {
+ showConfirmationDialog = false
+ navigateBack.invoke()
+ },
+ ) {
Text(text = stringResource(R.string.yes))
}
},
dismissButton = {
- TextButton(onClick = { showConfirmationDialog = false }) {
+ MifosTextButton(
+ onClick = { showConfirmationDialog = false },
+ ) {
Text(text = stringResource(R.string.no))
}
- }
+ },
)
}
- val viewModel: RegistrationViewModel = hiltViewModel()
- val uiState by viewModel.registrationVerificationUiState.collectAsStateWithLifecycle()
-
RegistrationVerificationScreen(
uiState = uiState,
verifyUser = { token, id -> viewModel.verifyUser(token, id) },
onVerified = onVerified,
- navigateBack = { showConfirmationDialog = true }
+ navigateBack = { showConfirmationDialog = true },
)
}
-
@Composable
-fun RegistrationVerificationScreen(
+private fun RegistrationVerificationScreen(
uiState: RegistrationUiState,
- verifyUser: (authenticationToken: String, requestID: String) -> Unit,
onVerified: () -> Unit,
- navigateBack: () -> Unit
+ navigateBack: () -> Unit,
+ verifyUser: (authenticationToken: String, requestID: String) -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
MifosScaffold(
topBarTitleResId = R.string.register,
navigateBack = navigateBack,
+ modifier = modifier,
content = { contentPadding ->
-
Box(
modifier = Modifier
.padding(contentPadding)
- .fillMaxSize()
+ .fillMaxSize(),
) {
-
RegistrationVerificationContent(verifyUser)
when (uiState) {
+ is RegistrationUiState.Initial -> Unit
- RegistrationUiState.Initial -> Unit
+ is RegistrationUiState.Loading -> MifosProgressIndicatorOverlay()
is RegistrationUiState.Error -> {
Toast.makeText(context, uiState.exception, Toast.LENGTH_SHORT).show()
}
- RegistrationUiState.Loading -> {
- MifosProgressIndicatorOverlay()
- }
-
- RegistrationUiState.Success -> {
- Toast.makeText(context, stringResource(R.string.verified), Toast.LENGTH_SHORT).show()
+ is RegistrationUiState.Success -> {
+ Toast.makeText(
+ context,
+ stringResource(R.string.verified),
+ Toast.LENGTH_SHORT,
+ ).show()
onVerified()
}
}
}
- }
+ },
)
}
@Composable
-fun RegistrationVerificationContent(verifyUser: (authenticationToken: String, requestID: String) -> Unit) {
-
- val context = LocalContext.current
-
+private fun RegistrationVerificationContent(
+ verifyUser: (authenticationToken: String, requestID: String) -> Unit,
+ modifier: Modifier = Modifier,
+) {
var requestID by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(""))
}
@@ -152,7 +166,6 @@ fun RegistrationVerificationContent(verifyUser: (authenticationToken: String, re
var authenticationTokenError by remember { mutableStateOf(false) }
fun validateInput(): Boolean {
-
var temp = true
if (requestID.text.isEmpty()) {
requestIDError = true
@@ -166,23 +179,24 @@ fun RegistrationVerificationContent(verifyUser: (authenticationToken: String, re
return temp
}
- Column(modifier = Modifier.fillMaxSize()) {
-
+ Column(
+ modifier = modifier.fillMaxSize(),
+ ) {
Image(
- painter = painterResource(R.drawable.mifos_logo),
+ painter = painterResource(R.drawable.feature_auth_mifos_logo),
contentDescription = null,
contentScale = ContentScale.Fit,
alignment = Alignment.Center,
modifier = Modifier
.padding(16.dp)
.height(100.dp)
- .fillMaxWidth()
+ .fillMaxWidth(),
)
Spacer(
modifier = Modifier
.fillMaxWidth()
- .height(80.dp)
+ .height(80.dp),
)
MifosOutlinedTextField(
@@ -197,9 +211,9 @@ fun RegistrationVerificationContent(verifyUser: (authenticationToken: String, re
keyboardType = KeyboardType.Number,
trailingIcon = {
if (requestIDError) {
- Icon(imageVector = Icons.Filled.Error, contentDescription = null)
+ Icon(imageVector = MifosIcons.Error, contentDescription = null)
}
- }
+ },
)
MifosOutlinedTextField(
@@ -214,52 +228,52 @@ fun RegistrationVerificationContent(verifyUser: (authenticationToken: String, re
keyboardType = KeyboardType.Number,
trailingIcon = {
if (authenticationTokenError) {
- Icon(imageVector = Icons.Filled.Error, contentDescription = null)
+ Icon(imageVector = MifosIcons.Error, contentDescription = null)
}
- }
+ },
)
- Button(
+ MifosButton(
+ textResId = R.string.verify,
onClick = {
- if (validateInput())
+ if (validateInput()) {
verifyUser(authenticationToken.toString(), requestID.toString())
+ }
},
- Modifier
+ modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, end = 16.dp, top = 12.dp),
contentPadding = PaddingValues(12.dp),
colors = ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.primary
- )
- ) {
- Text(text = stringResource(id = R.string.verify))
- }
+ containerColor = MaterialTheme.colorScheme.primary,
+ ),
+ )
}
}
-
-class RegistrationVerificationScreenPreviewProvider :
+internal class RegistrationVerificationScreenPreviewProvider :
PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
RegistrationUiState.Initial,
RegistrationUiState.Loading,
RegistrationUiState.Success,
- RegistrationUiState.Error(R.string.register)
+ RegistrationUiState.Error(R.string.register),
)
}
-@Preview(showSystemUi = true, showBackground = true)
+@DevicePreviews
@Composable
private fun RegistrationVerificationScreenPreview(
- @PreviewParameter(RegistrationVerificationScreenPreviewProvider::class) registrationUiState: RegistrationUiState
+ @PreviewParameter(RegistrationVerificationScreenPreviewProvider::class)
+ registrationUiState: RegistrationUiState,
) {
MifosMobileTheme {
RegistrationVerificationScreen(
uiState = registrationUiState,
verifyUser = { _, _ -> },
onVerified = {},
- navigateBack = { }
+ navigateBack = { },
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/utils/PasswordStrength.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/utils/PasswordStrength.kt
index 57a27dafa..9fbcb10d1 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/utils/PasswordStrength.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/utils/PasswordStrength.kt
@@ -1,77 +1,81 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.auth.registration.utils
import android.content.Context
import android.graphics.Color
import org.mifos.mobile.feature.auth.R
-enum class PasswordStrength(private var resId: Int, var color: Int) {
-
+enum class PasswordStrength(private val resId: Int, val color: Int) {
WEAK(R.string.password_strength_weak, Color.RED),
MEDIUM(R.string.password_strength_medium, Color.argb(255, 220, 185, 0)),
STRONG(R.string.password_strength_strong, Color.GREEN),
- VERY_STRONG(R.string.password_strength_very_strong, Color.BLUE);
+ VERY_STRONG(R.string.password_strength_very_strong, Color.BLUE),
+ ;
- fun getText(ctx: Context?): CharSequence? {
- return ctx?.getText(resId)
- }
+ fun getText(ctx: Context?): CharSequence? = ctx?.getText(resId)
companion object {
- // --------REQUIREMENTS--------
- private var REQUIRED_LENGTH = 8
- private var MAXIMUM_LENGTH = 15
- private var REQUIRE_SPECIAL_CHARACTERS = true
- private var REQUIRE_DIGITS = true
- private var REQUIRE_LOWER_CASE = true
- private var REQUIRE_UPPER_CASE = false
+ private const val REQUIRED_LENGTH = 8
+ private const val MAXIMUM_LENGTH = 15
+ private const val REQUIRE_SPECIAL_CHARACTERS = true
+ private const val REQUIRE_DIGITS = true
+ private const val REQUIRE_LOWER_CASE = true
+ private const val REQUIRE_UPPER_CASE = false
+
fun calculateStrength(password: String): PasswordStrength {
- var currentScore = 0
- var sawUpper = false
- var sawLower = false
- var sawDigit = false
- var sawSpecial = false
- for (element in password) {
- if (!sawSpecial && !Character.isLetterOrDigit(element)) {
- currentScore += 1
- sawSpecial = true
- } else {
- if (!sawDigit && Character.isDigit(element)) {
- currentScore += 1
- sawDigit = true
- } else {
- if (!sawUpper || !sawLower) {
- if (Character.isUpperCase(element)) {
- sawUpper = true
- } else {
- sawLower = true
- }
- if (sawUpper && sawLower) currentScore += 1
- }
- }
- }
+ val score = calculateScore(password)
+ return when {
+ score >= 3 -> VERY_STRONG
+ score == 2 -> STRONG
+ score == 1 -> MEDIUM
+ else -> WEAK
}
- if (password.length > REQUIRED_LENGTH) {
- if (REQUIRE_SPECIAL_CHARACTERS && !sawSpecial ||
- REQUIRE_UPPER_CASE && !sawUpper ||
- REQUIRE_LOWER_CASE && !sawLower ||
- REQUIRE_DIGITS && !sawDigit
- ) {
- currentScore = 1
- } else {
- currentScore = 2
- if (password.length > MAXIMUM_LENGTH) {
- currentScore = 3
- }
+ }
+
+ @Suppress("ComplexCondition")
+ private fun calculateScore(password: String): Int {
+ if (password.length <= REQUIRED_LENGTH) return 0
+
+ var score = 0
+ val (hasUpper, hasLower, hasDigit, hasSpecial) = password.fold(
+ BooleanArray(4),
+ ) { acc, char ->
+ when {
+ char.isUpperCase() -> acc[0] = true
+ char.isLowerCase() -> acc[1] = true
+ char.isDigit() -> acc[2] = true
+ !char.isLetterOrDigit() -> acc[3] = true
}
- } else {
- currentScore = 0
+ acc
}
- when (currentScore) {
- 0 -> return WEAK
- 1 -> return MEDIUM
- 2 -> return STRONG
- 3 -> return VERY_STRONG
+
+ if (hasUpper) score++
+ if (hasLower) score++
+ if (hasDigit) score++
+ if (hasSpecial) score++
+
+ if (REQUIRE_SPECIAL_CHARACTERS && !hasSpecial ||
+ REQUIRE_UPPER_CASE && !hasUpper ||
+ REQUIRE_LOWER_CASE && !hasLower ||
+ REQUIRE_DIGITS && !hasDigit
+ ) {
+ score = 1
+ } else {
+ score = 2
+ if (password.length > MAXIMUM_LENGTH) {
+ score = 3
+ }
}
- return VERY_STRONG
+
+ return score
}
}
}
diff --git a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/viewmodel/RegistrationViewModel.kt b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/viewmodel/RegistrationViewModel.kt
index 94fb7e5ce..a69dd5668 100644
--- a/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/viewmodel/RegistrationViewModel.kt
+++ b/feature/auth/src/main/java/org/mifos/mobile/feature/auth/registration/viewmodel/RegistrationViewModel.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.auth.registration.viewmodel
import androidx.lifecycle.ViewModel
@@ -12,8 +21,9 @@ import org.mifos.mobile.feature.auth.R
import javax.inject.Inject
@HiltViewModel
-class RegistrationViewModel @Inject constructor(private val userAuthRepositoryImp: UserAuthRepository) :
- ViewModel() {
+internal class RegistrationViewModel @Inject constructor(
+ private val userAuthRepositoryImp: UserAuthRepository,
+) : ViewModel() {
private val _registrationUiState =
MutableStateFlow(RegistrationUiState.Initial)
@@ -29,21 +39,22 @@ class RegistrationViewModel @Inject constructor(private val userAuthRepositoryIm
email: String,
firstName: String,
lastName: String,
+ countryCode: String,
mobileNumber: String,
password: String,
- username: String
+ username: String,
) {
viewModelScope.launch {
_registrationUiState.value = RegistrationUiState.Loading
userAuthRepositoryImp.registerUser(
- accountNumber,
- authenticationMode,
- email,
- firstName,
- lastName,
- mobileNumber,
- password,
- username
+ accountNumber = accountNumber,
+ authenticationMode = authenticationMode,
+ email = email,
+ firstName = firstName,
+ lastName = lastName,
+ mobileNumber = countryCode + mobileNumber,
+ password = password,
+ username = username,
).catch {
_registrationUiState.value =
RegistrationUiState.Error(R.string.could_not_register_user_error)
@@ -67,10 +78,9 @@ class RegistrationViewModel @Inject constructor(private val userAuthRepositoryIm
}
}
-
-sealed class RegistrationUiState {
+internal sealed class RegistrationUiState {
data class Error(val exception: Int) : RegistrationUiState()
data object Success : RegistrationUiState()
data object Loading : RegistrationUiState()
- data object Initial: RegistrationUiState()
+ data object Initial : RegistrationUiState()
}
diff --git a/feature/auth/src/main/res/drawable/ic_person_black_24dp.xml b/feature/auth/src/main/res/drawable/feature_auth_ic_person.xml
similarity index 52%
rename from feature/auth/src/main/res/drawable/ic_person_black_24dp.xml
rename to feature/auth/src/main/res/drawable/feature_auth_ic_person.xml
index 55495d5a0..a45776581 100644
--- a/feature/auth/src/main/res/drawable/ic_person_black_24dp.xml
+++ b/feature/auth/src/main/res/drawable/feature_auth_ic_person.xml
@@ -1,3 +1,13 @@
+
+
+
+
No Internet Connection
Register
diff --git a/feature/auth/src/main/res/values/validations.xml b/feature/auth/src/main/res/values/validations.xml
index 37e3b680a..cdebb2f54 100644
--- a/feature/auth/src/main/res/values/validations.xml
+++ b/feature/auth/src/main/res/values/validations.xml
@@ -1,4 +1,13 @@
+
5
6
diff --git a/feature/auth/src/test/java/org/mifos/mobile/feature/auth/ExampleUnitTest.kt b/feature/auth/src/test/java/org/mifos/mobile/feature/auth/ExampleUnitTest.kt
deleted file mode 100644
index 30c9328cd..000000000
--- a/feature/auth/src/test/java/org/mifos/mobile/feature/auth/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.mifos.mobile.feature.auth
-
-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/beneficiary/build.gradle.kts b/feature/beneficiary/build.gradle.kts
index cca3b949d..3a98116e0 100644
--- a/feature/beneficiary/build.gradle.kts
+++ b/feature/beneficiary/build.gradle.kts
@@ -1,21 +1,19 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
plugins {
alias(libs.plugins.mifos.android.feature)
alias(libs.plugins.mifos.android.library.compose)
- alias(libs.plugins.mifos.android.hilt)
}
android {
namespace = "org.mifos.mobile.feature.beneficiary"
}
-dependencies {
- implementation(projects.core.ui)
- implementation(projects.core.common)
- implementation(projects.core.model)
- implementation(projects.core.data)
-
- testImplementation(libs.junit)
- androidTestImplementation(libs.androidx.test.ext.junit)
- androidTestImplementation(libs.espresso.core)
- api(libs.androidx.fragment.ktx)
-}
\ No newline at end of file
+dependencies { }
\ No newline at end of file
diff --git a/feature/beneficiary/src/androidTest/java/org/mifos/mobile/feature/beneficiary/ExampleInstrumentedTest.kt b/feature/beneficiary/src/androidTest/java/org/mifos/mobile/feature/beneficiary/ExampleInstrumentedTest.kt
deleted file mode 100644
index db7e827d1..000000000
--- a/feature/beneficiary/src/androidTest/java/org/mifos/mobile/feature/beneficiary/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.mifos.mobile.feature.beneficiary
-
-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("org.mifos.mobile.feature.beneficiary.test", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/feature/beneficiary/src/main/AndroidManifest.xml b/feature/beneficiary/src/main/AndroidManifest.xml
index a5918e68a..1f9b243f0 100644
--- a/feature/beneficiary/src/main/AndroidManifest.xml
+++ b/feature/beneficiary/src/main/AndroidManifest.xml
@@ -1,4 +1,13 @@
+
\ No newline at end of file
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationContent.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryApplication/BeneficiaryApplicationContent.kt
similarity index 68%
rename from feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationContent.kt
rename to feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryApplication/BeneficiaryApplicationContent.kt
index 4c96b4b65..4e95f7f4b 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationContent.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryApplication/BeneficiaryApplicationContent.kt
@@ -1,4 +1,13 @@
-package org.mifos.mobile.feature.beneficiary.beneficiary_application
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.beneficiary.beneficiaryApplication
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@@ -19,55 +28,80 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import org.mifos.mobile.core.designsystem.components.MifosButton
import org.mifos.mobile.core.designsystem.components.MifosOutlinedTextField
-import org.mifos.mobile.core.designsystem.components.MifosTextButton
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
import org.mifos.mobile.core.model.entity.beneficiary.BeneficiaryPayload
import org.mifos.mobile.core.model.entity.templates.beneficiary.BeneficiaryTemplate
import org.mifos.mobile.core.model.enums.BeneficiaryState
import org.mifos.mobile.core.ui.component.MifosDropDownTextField
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.beneficiary.R
@Composable
-fun BeneficiaryApplicationContent(
+@Suppress("CyclomaticComplexMethod", "ComplexCondition")
+internal fun BeneficiaryApplicationContent(
prefilledBeneficiary: Beneficiary?,
beneficiaryTemplate: BeneficiaryTemplate,
beneficiaryState: BeneficiaryState,
- onSubmit: (BeneficiaryPayload) -> Unit
+ onSubmit: (BeneficiaryPayload) -> Unit,
+ modifier: Modifier = Modifier,
) {
- val scrollState = rememberScrollState()
-
- var accountType by rememberSaveable { mutableIntStateOf(prefilledBeneficiary?.accountType?.id ?: -1) }
+ var accountType by rememberSaveable {
+ mutableIntStateOf(
+ prefilledBeneficiary?.accountType?.id ?: -1,
+ )
+ }
var accountTypeError by rememberSaveable { mutableStateOf(null) }
- var accountNumber by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue(prefilledBeneficiary?.accountNumber ?: "")) }
+ var accountNumber by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(
+ TextFieldValue(prefilledBeneficiary?.accountNumber ?: ""),
+ )
+ }
var accountNumberError by rememberSaveable { mutableStateOf(null) }
- var officeName by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue(prefilledBeneficiary?.officeName ?: "")) }
+ var officeName by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(
+ TextFieldValue(prefilledBeneficiary?.officeName ?: ""),
+ )
+ }
var officeNameError by rememberSaveable { mutableStateOf(null) }
- var transferLimit by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue(prefilledBeneficiary?.transferLimit?.toString() ?: "")) }
+ var transferLimit by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(
+ TextFieldValue(prefilledBeneficiary?.transferLimit?.toString() ?: ""),
+ )
+ }
var transferLimitError by rememberSaveable { mutableStateOf(null) }
- var beneficiaryName by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue(prefilledBeneficiary?.name ?: "")) }
+ var beneficiaryName by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(
+ TextFieldValue(prefilledBeneficiary?.name ?: ""),
+ )
+ }
var beneficiaryNameError by rememberSaveable { mutableStateOf(null) }
fun validateFields() {
if (beneficiaryState != BeneficiaryState.UPDATE) {
accountTypeError = if (accountType == -1) R.string.select_account_type else null
- accountNumberError = if (accountNumber.text.trim().isEmpty()) R.string.enter_account_number else null
- officeNameError = if (officeName.text.trim().isEmpty()) R.string.enter_office_name else null
+ accountNumberError =
+ if (accountNumber.text.trim().isEmpty()) R.string.enter_account_number else null
+ officeNameError =
+ if (officeName.text.trim().isEmpty()) R.string.enter_office_name else null
}
transferLimitError = when {
transferLimit.text.trim().isEmpty() -> R.string.enter_transfer_limit
transferLimit.text.any { it.isLetter() } -> R.string.invalid_amount
- transferLimit.text.toDoubleOrNull()?.let { it % 1 != 0.0 } == true -> R.string.invalid_amount
+ transferLimit.text.toDoubleOrNull()
+ ?.let { it % 1 != 0.0 } == true -> R.string.invalid_amount
+
else -> null
}
- beneficiaryNameError = if (beneficiaryName.text.trim().isEmpty()) R.string.enter_beneficiary_name else null
+ beneficiaryNameError =
+ if (beneficiaryName.text.trim().isEmpty()) R.string.enter_beneficiary_name else null
}
LaunchedEffect(key1 = accountType) {
@@ -90,18 +124,23 @@ fun BeneficiaryApplicationContent(
beneficiaryNameError = null
}
- Column(modifier = Modifier
- .fillMaxSize()
- .verticalScroll(rememberScrollState())
- .padding(16.dp)
+ Column(
+ modifier = modifier
+ .fillMaxSize()
+ .verticalScroll(rememberScrollState())
+ .padding(16.dp),
) {
MifosDropDownTextField(
- optionsList = beneficiaryTemplate.accountTypeOptions?.mapNotNull { it.value } ?: listOf(),
+ optionsList = beneficiaryTemplate.accountTypeOptions?.mapNotNull { it.value }
+ ?: listOf(),
labelResId = R.string.select_account_type,
- onClick = { index, _ -> accountType = beneficiaryTemplate.accountTypeOptions?.filter{ it.value != null }?.get(index)?.id ?: -1 },
+ onClick = { index, _ ->
+ accountType = beneficiaryTemplate.accountTypeOptions?.filter { it.value != null }
+ ?.get(index)?.id ?: -1
+ },
error = accountTypeError != null && beneficiaryState != BeneficiaryState.UPDATE,
isEnabled = beneficiaryState != BeneficiaryState.UPDATE,
- supportingText = accountTypeError?.let { stringResource(id = it) }
+ supportingText = accountTypeError?.let { stringResource(id = it) },
)
MifosOutlinedTextField(
@@ -111,7 +150,7 @@ fun BeneficiaryApplicationContent(
label = R.string.account_number,
error = accountNumberError != null && beneficiaryState != BeneficiaryState.UPDATE,
enabled = beneficiaryState != BeneficiaryState.UPDATE,
- supportingText = accountNumberError?.let { stringResource(id = it) }
+ supportingText = accountNumberError?.let { stringResource(id = it) },
)
MifosOutlinedTextField(
@@ -121,7 +160,7 @@ fun BeneficiaryApplicationContent(
label = R.string.office_name,
error = officeNameError != null && beneficiaryState != BeneficiaryState.UPDATE,
enabled = beneficiaryState != BeneficiaryState.UPDATE,
- supportingText = officeNameError?.let { stringResource(id = it) }
+ supportingText = officeNameError?.let { stringResource(id = it) },
)
MifosOutlinedTextField(
@@ -131,7 +170,7 @@ fun BeneficiaryApplicationContent(
label = R.string.transfer_limit,
keyboardType = KeyboardType.Number,
error = transferLimitError != null,
- supportingText = transferLimitError?.let { stringResource(id = it) }
+ supportingText = transferLimitError?.let { stringResource(id = it) },
)
MifosOutlinedTextField(
@@ -140,36 +179,42 @@ fun BeneficiaryApplicationContent(
onValueChange = { beneficiaryName = it },
label = R.string.beneficiary_name,
error = beneficiaryNameError != null,
- supportingText = beneficiaryNameError?.let { stringResource(id = it) }
+ supportingText = beneficiaryNameError?.let { stringResource(id = it) },
)
Spacer(modifier = Modifier.height(10.dp))
- MifosTextButton(
+ MifosButton(
modifier = Modifier.fillMaxWidth(),
textResId = R.string.submit_beneficiary,
onClick = {
validateFields()
- if ((beneficiaryState != BeneficiaryState.UPDATE && accountTypeError == null && accountNumberError == null && officeNameError == null) &&
- transferLimitError == null && beneficiaryNameError == null) {
+ if ((
+ beneficiaryState != BeneficiaryState.UPDATE && accountTypeError == null &&
+ accountNumberError == null && officeNameError == null
+ ) &&
+ transferLimitError == null && beneficiaryNameError == null
+ ) {
onSubmit(
BeneficiaryPayload(
name = beneficiaryName.text,
accountNumber = accountNumber.text,
transferLimit = transferLimit.text.toFloat(),
officeName = officeName.text,
- accountType = accountType
- )
+ accountType = accountType,
+ ),
)
- } else if (beneficiaryState == BeneficiaryState.UPDATE && transferLimitError == null && beneficiaryNameError == null) {
+ } else if (beneficiaryState == BeneficiaryState.UPDATE &&
+ transferLimitError == null && beneficiaryNameError == null
+ ) {
onSubmit(
BeneficiaryPayload(
name = beneficiaryName.text,
accountNumber = accountNumber.text,
transferLimit = transferLimit.text.toFloat(),
officeName = officeName.text,
- accountType = accountType
- )
+ accountType = accountType,
+ ),
)
}
},
@@ -177,10 +222,9 @@ fun BeneficiaryApplicationContent(
}
}
-
@Composable
-@Preview(showSystemUi = true)
-fun BeneficiaryApplicationContentPreview() {
+@DevicePreviews
+private fun BeneficiaryApplicationContentPreview() {
MifosMobileTheme {
BeneficiaryApplicationContent(
prefilledBeneficiary = null,
@@ -189,4 +233,4 @@ fun BeneficiaryApplicationContentPreview() {
onSubmit = {},
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationScreen.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryApplication/BeneficiaryApplicationScreen.kt
similarity index 72%
rename from feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationScreen.kt
rename to feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryApplication/BeneficiaryApplicationScreen.kt
index ad86c548d..863e651bd 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_application/BeneficiaryApplicationScreen.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryApplication/BeneficiaryApplicationScreen.kt
@@ -1,15 +1,21 @@
-package org.mifos.mobile.feature.beneficiary.beneficiary_application
-
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.beneficiary.beneficiaryApplication
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.hilt.navigation.compose.hiltViewModel
@@ -23,97 +29,106 @@ import org.mifos.mobile.core.model.entity.templates.beneficiary.BeneficiaryTempl
import org.mifos.mobile.core.model.enums.BeneficiaryState
import org.mifos.mobile.core.ui.component.MifosErrorComponent
import org.mifos.mobile.core.ui.component.MifosProgressIndicator
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.beneficiary.R
-
@Composable
-fun BeneficiaryApplicationScreen(
- viewModel: BeneficiaryApplicationViewModel = hiltViewModel(),
+internal fun BeneficiaryApplicationScreen(
navigateBack: () -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: BeneficiaryApplicationViewModel = hiltViewModel(),
) {
val beneficiaryState by viewModel.beneficiaryState.collectAsStateWithLifecycle()
val beneficiary by viewModel.beneficiary.collectAsStateWithLifecycle()
val uiState by viewModel.beneficiaryUiState.collectAsStateWithLifecycle()
- LaunchedEffect(key1 = Unit) {
- viewModel.loadBeneficiaryTemplate()
- }
-
- BeneficiaryApplicationScreen (
+ BeneficiaryApplicationScreen(
uiState = uiState,
- navigateBack = navigateBack,
beneficiaryState = beneficiaryState,
beneficiary = beneficiary,
- onRetry = { viewModel.loadBeneficiaryTemplate() },
- onSubmit = { viewModel.submitBeneficiary(it) }
+ navigateBack = navigateBack,
+ onRetry = viewModel::loadBeneficiaryTemplate,
+ onSubmit = viewModel::submitBeneficiary,
+ modifier = modifier,
)
}
@Composable
-fun BeneficiaryApplicationScreen(
+private fun BeneficiaryApplicationScreen(
uiState: BeneficiaryApplicationUiState,
- navigateBack: () -> Unit,
beneficiaryState: BeneficiaryState,
beneficiary: Beneficiary?,
+ navigateBack: () -> Unit,
onRetry: () -> Unit,
- onSubmit: (BeneficiaryPayload) -> Unit
+ onSubmit: (BeneficiaryPayload) -> Unit,
+ modifier: Modifier = Modifier,
) {
val context = LocalContext.current
+
MifosScaffold(
- topBarTitleResId = when(beneficiaryState) {
+ topBarTitleResId = when (beneficiaryState) {
BeneficiaryState.UPDATE -> R.string.update_beneficiary
else -> R.string.add_beneficiary
},
navigateBack = navigateBack,
+ modifier = modifier,
content = { paddingValues ->
- Box(modifier = Modifier.padding(paddingValues = paddingValues)) {
- when(uiState) {
+ Box(
+ modifier = Modifier
+ .padding(paddingValues = paddingValues),
+ ) {
+ when (uiState) {
is BeneficiaryApplicationUiState.Error -> {
MifosErrorComponent(
isNetworkConnected = Network.isConnected(context),
isRetryEnabled = true,
onRetry = onRetry,
- message = stringResource(id = uiState.errorResId)
+ message = stringResource(id = uiState.errorResId),
)
}
+
is BeneficiaryApplicationUiState.Loading -> {
MifosProgressIndicator()
}
+
is BeneficiaryApplicationUiState.Success -> {
navigateBack()
}
+
is BeneficiaryApplicationUiState.Template -> {
BeneficiaryApplicationContent(
prefilledBeneficiary = beneficiary,
beneficiaryTemplate = uiState.beneficiaryTemplate,
beneficiaryState = beneficiaryState,
- onSubmit = onSubmit
+ onSubmit = onSubmit,
)
}
}
}
- }
+ },
)
}
-@Preview(showSystemUi = true)
+@DevicePreviews
@Composable
-fun BeneficiaryApplicationScreenPreview(
- @PreviewParameter(BeneficiaryApplicationUiPreview::class) uiState: BeneficiaryApplicationUiState
+private fun BeneficiaryApplicationScreenPreview(
+ @PreviewParameter(BeneficiaryApplicationUiPreview::class)
+ uiState: BeneficiaryApplicationUiState,
) {
MifosMobileTheme {
BeneficiaryApplicationScreen(
uiState = uiState,
+ beneficiaryState = BeneficiaryState.CREATE_QR,
+ beneficiary = Beneficiary(),
navigateBack = { },
onRetry = { },
- beneficiary = Beneficiary(),
- beneficiaryState = BeneficiaryState.CREATE_QR,
- onSubmit = { }
+ onSubmit = { },
)
}
}
-class BeneficiaryApplicationUiPreview : PreviewParameterProvider {
+internal class BeneficiaryApplicationUiPreview :
+ PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
BeneficiaryApplicationUiState.Template(BeneficiaryTemplate()),
@@ -121,5 +136,3 @@ class BeneficiaryApplicationUiPreview : PreviewParameterProvider(BeneficiaryApplicationUiState.Loading)
+ private val _beneficiaryUiState = MutableStateFlow(Loading)
val beneficiaryUiState: StateFlow get() = _beneficiaryUiState
- private val beneficiaryId = savedStateHandle.getStateFlow(key = BENEFICIARY_ID, initialValue = null)
+ private val beneficiaryId = savedStateHandle.getStateFlow(
+ key = BENEFICIARY_ID,
+ initialValue = null,
+ )
- val beneficiaryState = savedStateHandle.getStateFlow(key = BENEFICIARY_STATE, initialValue = BeneficiaryState.CREATE_QR)
+ val beneficiaryState = savedStateHandle.getStateFlow(
+ key = BENEFICIARY_STATE,
+ initialValue = BeneficiaryState.CREATE_QR,
+ )
val beneficiary: StateFlow = beneficiaryId
.flatMapLatest {
@@ -47,15 +62,19 @@ class BeneficiaryApplicationViewModel @Inject constructor(
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
- initialValue = null
+ initialValue = null,
)
+ init {
+ loadBeneficiaryTemplate()
+ }
+
fun loadBeneficiaryTemplate() {
viewModelScope.launch {
- _beneficiaryUiState.value = BeneficiaryApplicationUiState.Loading
+ _beneficiaryUiState.value = Loading
beneficiaryRepositoryImp.beneficiaryTemplate().catch {
- _beneficiaryUiState.value =
- BeneficiaryApplicationUiState.Error(R.string.error_fetching_beneficiary_template)
+ _beneficiaryUiState.value =
+ BeneficiaryApplicationUiState.Error(R.string.error_fetching_beneficiary_template)
}.collect {
_beneficiaryUiState.value = BeneficiaryApplicationUiState.Template(it)
}
@@ -63,21 +82,22 @@ class BeneficiaryApplicationViewModel @Inject constructor(
}
fun submitBeneficiary(beneficiaryPayload: BeneficiaryPayload) {
- when(beneficiaryState.value) {
+ when (beneficiaryState.value) {
BeneficiaryState.UPDATE -> updateBeneficiary(
beneficiaryId = beneficiary.value?.id?.toLong(),
payload = BeneficiaryUpdatePayload(
name = beneficiaryPayload.name,
- transferLimit = beneficiaryPayload.transferLimit ?: 0F
- )
+ transferLimit = beneficiaryPayload.transferLimit ?: 0F,
+ ),
)
+
else -> createBeneficiary(payload = beneficiaryPayload)
}
}
private fun createBeneficiary(payload: BeneficiaryPayload?) {
viewModelScope.launch {
- _beneficiaryUiState.value = BeneficiaryApplicationUiState.Loading
+ _beneficiaryUiState.value = Loading
beneficiaryRepositoryImp.createBeneficiary(payload).catch {
_beneficiaryUiState.value =
BeneficiaryApplicationUiState.Error(R.string.error_creating_beneficiary)
@@ -89,7 +109,7 @@ class BeneficiaryApplicationViewModel @Inject constructor(
private fun updateBeneficiary(beneficiaryId: Long?, payload: BeneficiaryUpdatePayload?) {
viewModelScope.launch {
- _beneficiaryUiState.value = BeneficiaryApplicationUiState.Loading
+ _beneficiaryUiState.value = Loading
beneficiaryRepositoryImp.updateBeneficiary(beneficiaryId, payload).catch {
_beneficiaryUiState.value =
BeneficiaryApplicationUiState.Error(R.string.error_updating_beneficiary)
@@ -98,13 +118,13 @@ class BeneficiaryApplicationViewModel @Inject constructor(
}
}
}
-
}
-sealed class BeneficiaryApplicationUiState {
- data class Template(val beneficiaryTemplate: BeneficiaryTemplate): BeneficiaryApplicationUiState()
- data object Loading: BeneficiaryApplicationUiState()
- data object Success: BeneficiaryApplicationUiState()
- data class Error(val errorResId: Int): BeneficiaryApplicationUiState()
-}
+internal sealed class BeneficiaryApplicationUiState {
+ data class Template(val beneficiaryTemplate: BeneficiaryTemplate) :
+ BeneficiaryApplicationUiState()
+ data object Loading : BeneficiaryApplicationUiState()
+ data object Success : BeneficiaryApplicationUiState()
+ data class Error(val errorResId: Int) : BeneficiaryApplicationUiState()
+}
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailContent.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryDetail/BeneficiaryDetailContent.kt
similarity index 71%
rename from feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailContent.kt
rename to feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryDetail/BeneficiaryDetailContent.kt
index 9b35c465c..7210c8fe3 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailContent.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryDetail/BeneficiaryDetailContent.kt
@@ -1,4 +1,13 @@
-package org.mifos.mobile.feature.beneficiary.beneficiary_detail
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.beneficiary.beneficiaryDetail
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
@@ -12,29 +21,29 @@ import androidx.compose.material3.OutlinedCard
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
import org.mifos.mobile.core.ui.component.MifosTitleDescSingleLineEqual
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.beneficiary.R
@Composable
-fun BeneficiaryDetailContent(
- beneficiary: Beneficiary?
+internal fun BeneficiaryDetailContent(
+ beneficiary: Beneficiary?,
+ modifier: Modifier = Modifier,
) {
Column(
- modifier = Modifier
+ modifier = modifier
.fillMaxSize()
- .padding(16.dp)
+ .padding(16.dp),
) {
-
MifosTitleDescSingleLineEqual(
modifier = Modifier
.padding(horizontal = 4.dp)
.padding(4.dp),
title = stringResource(id = R.string.beneficiary_name),
- description = beneficiary?.name.toString()
+ description = beneficiary?.name.toString(),
)
MifosTitleDescSingleLineEqual(
@@ -42,54 +51,57 @@ fun BeneficiaryDetailContent(
.padding(horizontal = 4.dp)
.padding(4.dp),
title = stringResource(id = R.string.account_number),
- description = beneficiary?.accountNumber.toString()
+ description = beneficiary?.accountNumber.toString(),
)
Spacer(modifier = Modifier.height(24.dp))
OutlinedCard(
- modifier = Modifier.fillMaxWidth(), colors = CardDefaults.outlinedCardColors(
- containerColor = MaterialTheme.colorScheme.background
- )
+ modifier = Modifier.fillMaxWidth(),
+ colors = CardDefaults.outlinedCardColors(
+ containerColor = MaterialTheme.colorScheme.background,
+ ),
) {
Column(
- modifier = Modifier.padding(8.dp)
+ modifier = Modifier.padding(8.dp),
) {
MifosTitleDescSingleLineEqual(
modifier = Modifier.padding(vertical = 4.dp),
title = stringResource(id = R.string.client_name),
- description = beneficiary?.clientName.toString()
+ description = beneficiary?.clientName.toString(),
)
MifosTitleDescSingleLineEqual(
modifier = Modifier.padding(vertical = 4.dp),
title = stringResource(id = R.string.account_type),
- description = beneficiary?.accountType.toString()
+ description = beneficiary?.accountType.toString(),
)
MifosTitleDescSingleLineEqual(
modifier = Modifier.padding(vertical = 4.dp),
title = stringResource(id = R.string.transfer_limit),
- description = beneficiary?.transferLimit.toString()
+ description = beneficiary?.transferLimit.toString(),
)
MifosTitleDescSingleLineEqual(
modifier = Modifier.padding(vertical = 4.dp),
title = stringResource(id = R.string.office_name),
- description = beneficiary?.officeName.toString()
+ description = beneficiary?.officeName.toString(),
)
}
}
}
}
-
-@Preview(showSystemUi = true)
+@DevicePreviews
@Composable
-fun PreviewBeneficiaryDetailContent(modifier: Modifier = Modifier) {
+private fun PreviewBeneficiaryDetailContent(
+ modifier: Modifier = Modifier,
+) {
MifosMobileTheme {
BeneficiaryDetailContent(
- beneficiary = Beneficiary()
+ beneficiary = Beneficiary(),
+ modifier = modifier,
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailScreen.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryDetail/BeneficiaryDetailScreen.kt
similarity index 74%
rename from feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailScreen.kt
rename to feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryDetail/BeneficiaryDetailScreen.kt
index 14acd3472..782bdf52c 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailScreen.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryDetail/BeneficiaryDetailScreen.kt
@@ -1,12 +1,18 @@
-package org.mifos.mobile.feature.beneficiary.beneficiary_detail
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.beneficiary.beneficiaryDetail
import android.widget.Toast
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ArrowBack
-import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -26,22 +32,24 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import org.mifos.mobile.core.designsystem.icons.MifosIcons
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
import org.mifos.mobile.core.ui.component.MifosAlertDialog
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.beneficiary.R
@Composable
-fun BeneficiaryDetailScreen(
- viewModel: BeneficiaryDetailViewModel = hiltViewModel(),
+internal fun BeneficiaryDetailScreen(
navigateBack: () -> Unit,
updateBeneficiary: (beneficiary: Beneficiary?) -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: BeneficiaryDetailViewModel = hiltViewModel(),
) {
val uiState by viewModel.beneficiaryDetailsUiStates.collectAsStateWithLifecycle()
val beneficiary by viewModel.beneficiary.collectAsStateWithLifecycle()
@@ -50,22 +58,22 @@ fun BeneficiaryDetailScreen(
beneficiary = beneficiary,
uiState = uiState,
navigateBack = navigateBack,
+ modifier = modifier,
updateBeneficiary = {
- updateBeneficiary(viewModel.getBeneficiary())
+ updateBeneficiary(beneficiary)
},
- deleteBeneficiary = {
- viewModel.deleteBeneficiary(it)
- }
+ deleteBeneficiary = viewModel::deleteBeneficiary,
)
}
@Composable
-fun BeneficiaryDetailScreen(
+private fun BeneficiaryDetailScreen(
beneficiary: Beneficiary?,
uiState: BeneficiaryDetailsUiState,
navigateBack: () -> Unit,
updateBeneficiary: () -> Unit,
deleteBeneficiary: (id: Long?) -> Unit,
+ modifier: Modifier = Modifier,
) {
var openDropdown by rememberSaveable { mutableStateOf(false) }
var showAlert by rememberSaveable { mutableStateOf(false) }
@@ -88,7 +96,7 @@ fun BeneficiaryDetailScreen(
},
confirmationText = context.getString(R.string.delete),
dialogTitle = context.getString(R.string.delete_beneficiary),
- dialogText = context.getString(R.string.delete_beneficiary_confirmation) + "?"
+ dialogText = context.getString(R.string.delete_beneficiary_confirmation) + "?",
)
}
@@ -103,15 +111,16 @@ fun BeneficiaryDetailScreen(
},
showAlert = {
showAlert = true
- }
+ },
)
- }
+ },
+ modifier = modifier,
) {
Box(
- modifier = Modifier.padding(it)
+ modifier = Modifier.padding(it),
) {
BeneficiaryDetailContent(
- beneficiary = beneficiary
+ beneficiary = beneficiary,
)
when (uiState) {
@@ -125,7 +134,7 @@ fun BeneficiaryDetailScreen(
Toast.makeText(
context,
stringResource(id = R.string.beneficiary_deleted_successfully),
- Toast.LENGTH_SHORT
+ Toast.LENGTH_SHORT,
).show()
navigateBack.invoke()
}
@@ -140,7 +149,6 @@ fun BeneficiaryDetailScreen(
showBeneficiaryNullError = false
}
}
-
}
}
}
@@ -152,7 +160,8 @@ private fun BeneficiaryDetailTopAppBar(
navigateBack: () -> Unit,
updateBeneficiaryClicked: () -> Unit,
updateDropdownValue: (value: Boolean) -> Boolean,
- showAlert: () -> Unit
+ showAlert: () -> Unit,
+ modifier: Modifier = Modifier,
) {
var openDropdown by rememberSaveable {
mutableStateOf(false)
@@ -160,10 +169,11 @@ private fun BeneficiaryDetailTopAppBar(
TopAppBar(
title = { Text(text = stringResource(id = R.string.beneficiary_detail)) },
+ modifier = modifier,
navigationIcon = {
IconButton(onClick = { navigateBack.invoke() }) {
Icon(
- imageVector = Icons.Filled.ArrowBack,
+ imageVector = MifosIcons.ArrowBack,
contentDescription = "Back Arrow",
tint = if (isSystemInDarkTheme()) Color.White else Color.Black,
)
@@ -171,61 +181,56 @@ private fun BeneficiaryDetailTopAppBar(
},
actions = {
IconButton(
- onClick = { openDropdown = updateDropdownValue.invoke(!openDropdown) }
+ onClick = { openDropdown = updateDropdownValue.invoke(!openDropdown) },
) {
Icon(
- imageVector = Icons.Default.MoreVert,
+ imageVector = MifosIcons.MoreVert,
contentDescription = "More",
- tint = if (isSystemInDarkTheme()) Color.White else Color.Black,
)
}
- if (openDropdown) {
- DropdownMenu(
- expanded = openDropdown,
- onDismissRequest = {
+ DropdownMenu(
+ expanded = openDropdown,
+ onDismissRequest = {
+ openDropdown = updateDropdownValue.invoke(!openDropdown)
+ },
+ ) {
+ DropdownMenuItem(
+ text = { Text(text = stringResource(id = R.string.update_beneficiary)) },
+ onClick = {
openDropdown = updateDropdownValue.invoke(!openDropdown)
- }
- ) {
- DropdownMenuItem(
- text = { Text(text = stringResource(id = R.string.update_beneficiary)) },
- onClick = {
- openDropdown = updateDropdownValue.invoke(!openDropdown)
- updateBeneficiaryClicked.invoke()
- }
- )
- DropdownMenuItem(
- text = { Text(text = stringResource(id = R.string.delete_beneficiary)) },
- onClick = {
- openDropdown = updateDropdownValue.invoke(!openDropdown)
- showAlert.invoke()
- }
- )
- }
+ updateBeneficiaryClicked.invoke()
+ },
+ )
+ DropdownMenuItem(
+ text = { Text(text = stringResource(id = R.string.delete_beneficiary)) },
+ onClick = {
+ openDropdown = updateDropdownValue.invoke(!openDropdown)
+ showAlert.invoke()
+ },
+ )
}
},
- colors = TopAppBarDefaults.topAppBarColors(
- containerColor = if (isSystemInDarkTheme()) Color(0xFF1B1B1F)
- else Color(0xFFFEFBFF)
- )
+ colors = TopAppBarDefaults.topAppBarColors(),
)
}
-class BeneficiaryDetailScreenUiStatesParameterProvider :
+internal class BeneficiaryDetailScreenUiStatesParameterProvider :
PreviewParameterProvider {
override val values: Sequence
get() = sequenceOf(
BeneficiaryDetailsUiState.Initial,
BeneficiaryDetailsUiState.Loading,
BeneficiaryDetailsUiState.DeletedSuccessfully,
- BeneficiaryDetailsUiState.ShowError(R.string.error_creating_beneficiary)
+ BeneficiaryDetailsUiState.ShowError(R.string.error_creating_beneficiary),
)
}
@Composable
-@Preview(showSystemUi = true)
-fun PreviewBeneficiaryDetailScreen(
- @PreviewParameter(BeneficiaryDetailScreenUiStatesParameterProvider::class) beneficiaryDetailsUiState : BeneficiaryDetailsUiState
+@DevicePreviews
+private fun PreviewBeneficiaryDetailScreen(
+ @PreviewParameter(BeneficiaryDetailScreenUiStatesParameterProvider::class)
+ beneficiaryDetailsUiState: BeneficiaryDetailsUiState,
) {
val beneficiary = Beneficiary(
id = 1234,
@@ -234,7 +239,7 @@ fun PreviewBeneficiaryDetailScreen(
clientName = "Tom carl",
accountType = null,
transferLimit = 2003030.00,
- accountNumber = "2345678"
+ accountNumber = "2345678",
)
MifosMobileTheme {
BeneficiaryDetailScreen(
@@ -246,6 +251,3 @@ fun PreviewBeneficiaryDetailScreen(
)
}
}
-
-
-
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailViewModel.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryDetail/BeneficiaryDetailViewModel.kt
similarity index 73%
rename from feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailViewModel.kt
rename to feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryDetail/BeneficiaryDetailViewModel.kt
index 1e77e7ba4..a146e72e1 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_detail/BeneficiaryDetailViewModel.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryDetail/BeneficiaryDetailViewModel.kt
@@ -1,4 +1,13 @@
-package org.mifos.mobile.feature.beneficiary.beneficiary_detail
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.beneficiary.beneficiaryDetail
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
@@ -15,20 +24,23 @@ import kotlinx.coroutines.launch
import org.mifos.mobile.core.data.repository.BeneficiaryRepository
import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
import org.mifos.mobile.feature.beneficiary.R
+import org.mifos.mobile.feature.beneficiary.beneficiaryDetail.BeneficiaryDetailsUiState.Initial
import org.mifos.mobile.feature.beneficiary.navigation.BENEFICIARY_ID
import javax.inject.Inject
@HiltViewModel
-class BeneficiaryDetailViewModel @Inject constructor(
+internal class BeneficiaryDetailViewModel @Inject constructor(
private val beneficiaryRepositoryImp: BeneficiaryRepository,
- savedStateHandle: SavedStateHandle
+ savedStateHandle: SavedStateHandle,
) : ViewModel() {
- private val _beneficiaryDetailsUiStates =
- MutableStateFlow(BeneficiaryDetailsUiState.Initial)
+ private val _beneficiaryDetailsUiStates = MutableStateFlow(Initial)
val beneficiaryDetailsUiStates: StateFlow get() = _beneficiaryDetailsUiStates
- private val beneficiaryId = savedStateHandle.getStateFlow(key = BENEFICIARY_ID, initialValue = null)
+ private val beneficiaryId = savedStateHandle.getStateFlow(
+ key = BENEFICIARY_ID,
+ initialValue = null,
+ )
val beneficiary: StateFlow = beneficiaryId
.flatMapLatest {
@@ -40,13 +52,9 @@ class BeneficiaryDetailViewModel @Inject constructor(
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
- initialValue = null
+ initialValue = null,
)
- fun getBeneficiary(): Beneficiary? {
- return beneficiary.value
- }
-
fun deleteBeneficiary(beneficiaryId: Long?) {
viewModelScope.launch {
_beneficiaryDetailsUiStates.value = BeneficiaryDetailsUiState.Loading
@@ -60,12 +68,9 @@ class BeneficiaryDetailViewModel @Inject constructor(
}
}
-
-
-sealed class BeneficiaryDetailsUiState {
+internal sealed class BeneficiaryDetailsUiState {
data object Initial : BeneficiaryDetailsUiState()
data object Loading : BeneficiaryDetailsUiState()
data object DeletedSuccessfully : BeneficiaryDetailsUiState()
data class ShowError(val message: Int) : BeneficiaryDetailsUiState()
}
-
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListContent.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryList/BeneficiaryListContent.kt
similarity index 70%
rename from feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListContent.kt
rename to feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryList/BeneficiaryListContent.kt
index c868b0702..7970fe853 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListContent.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryList/BeneficiaryListContent.kt
@@ -1,4 +1,13 @@
-package org.mifos.mobile.feature.beneficiary.beneficiary_list
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.beneficiary.beneficiaryList
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -9,7 +18,7 @@ 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.itemsIndexed
+import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
@@ -19,25 +28,26 @@ 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.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
+import org.mifos.mobile.core.ui.utils.DevicePreviews
@Composable
fun ShowBeneficiary(
beneficiaryList: List,
- onClick: (beneficiaryId: Int) -> Unit
+ onClick: (beneficiaryId: Int) -> Unit,
+ modifier: Modifier = Modifier,
) {
Box(
- modifier = Modifier
- .fillMaxSize()
+ modifier = modifier
+ .fillMaxSize(),
) {
LazyColumn {
- itemsIndexed(beneficiaryList) { index, beneficiary ->
+ items(beneficiaryList) { beneficiary ->
BeneficiaryItem(
beneficiary = beneficiary,
- onClick = { onClick(beneficiary.id ?: -1) }
+ onClick = { onClick(beneficiary.id ?: -1) },
)
}
}
@@ -47,63 +57,62 @@ fun ShowBeneficiary(
@Composable
fun BeneficiaryItem(
beneficiary: Beneficiary,
- onClick: () -> Unit
+ onClick: () -> Unit,
+ modifier: Modifier = Modifier,
) {
Card(
- modifier = Modifier.fillMaxWidth(),
+ modifier = modifier.fillMaxWidth(),
shape = RoundedCornerShape(0.dp),
onClick = onClick,
colors = CardDefaults.cardColors(
- containerColor = Color.Transparent
- )
+ containerColor = Color.Transparent,
+ ),
) {
Column(
modifier = Modifier
.fillMaxWidth()
- .padding(horizontal = 8.dp, vertical = 16.dp)
+ .padding(horizontal = 8.dp, vertical = 16.dp),
) {
Text(
text = "${beneficiary.name}",
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium,
)
Row(
modifier = Modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.SpaceBetween
+ horizontalArrangement = Arrangement.SpaceBetween,
) {
Text(
text = "${beneficiary.id}",
style = MaterialTheme.typography.labelMedium,
- color = MaterialTheme.colorScheme.onBackground.copy(alpha = .7f)
+ color = MaterialTheme.colorScheme.onBackground.copy(alpha = .7f),
)
Text(
text = "${beneficiary.officeName}",
style = MaterialTheme.typography.labelMedium,
- color = MaterialTheme.colorScheme.onBackground.copy(alpha = .7f)
+ color = MaterialTheme.colorScheme.onBackground.copy(alpha = .7f),
)
}
-
}
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.height(0.2.dp),
- color = MaterialTheme.colorScheme.onBackground.copy(alpha = .3f)
+ color = MaterialTheme.colorScheme.onBackground.copy(alpha = .3f),
)
-
}
}
@Composable
-@Preview(showSystemUi = true)
+@DevicePreviews
fun PreviewBeneficiaryListEmpty(modifier: Modifier = Modifier) {
val beneficiary = Beneficiary(name = "Victor", id = 242344343, officeName = "Main office")
MifosMobileTheme {
BeneficiaryItem(
beneficiary = beneficiary,
- onClick = {}
+ onClick = {},
+ modifier = modifier,
)
}
}
-
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListScreen.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryList/BeneficiaryListScreen.kt
similarity index 81%
rename from feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListScreen.kt
rename to feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryList/BeneficiaryListScreen.kt
index 2fde51c3d..9c343f634 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiary_list/BeneficiaryListScreen.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/beneficiaryList/BeneficiaryListScreen.kt
@@ -1,4 +1,13 @@
-package org.mifos.mobile.feature.beneficiary.beneficiary_list
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.beneficiary.beneficiaryList
import android.widget.Toast
import androidx.compose.foundation.layout.Box
@@ -18,7 +27,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.hilt.navigation.compose.hiltViewModel
@@ -31,44 +39,43 @@ import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
import org.mifos.mobile.core.ui.component.EmptyDataView
import org.mifos.mobile.core.ui.component.MifosErrorComponent
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.beneficiary.R
@Composable
-fun BeneficiaryListScreen(
- viewModel: BeneficiaryListViewModel = hiltViewModel(),
+internal fun BeneficiaryListScreen(
navigateBack: () -> Unit,
addBeneficiaryClicked: () -> Unit,
onBeneficiaryItemClick: (position: Int) -> Unit,
+ modifier: Modifier = Modifier,
+ viewModel: BeneficiaryListViewModel = hiltViewModel(),
) {
val uiState by viewModel.beneficiaryListUiState.collectAsStateWithLifecycle()
val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle()
- LaunchedEffect(key1 = Unit) {
- viewModel.loadBeneficiaries()
- }
-
BeneficiaryListScreen(
uiState = uiState,
navigateBack = navigateBack,
addBeneficiaryClicked = addBeneficiaryClicked,
onBeneficiaryItemClick = onBeneficiaryItemClick,
- retryLoadingBeneficiary = { viewModel.loadBeneficiaries() },
+ retryLoadingBeneficiary = viewModel::loadBeneficiaries,
isRefreshing = isRefreshing,
- refresh = { viewModel.refresh() }
+ refresh = viewModel::refresh,
+ modifier = modifier,
)
}
-
@OptIn(ExperimentalMaterial3Api::class)
@Composable
-fun BeneficiaryListScreen(
+private fun BeneficiaryListScreen(
uiState: BeneficiaryListUiState,
+ isRefreshing: Boolean,
navigateBack: () -> Unit,
addBeneficiaryClicked: () -> Unit,
onBeneficiaryItemClick: (position: Int) -> Unit,
retryLoadingBeneficiary: () -> Unit,
- isRefreshing: Boolean,
refresh: () -> Unit,
+ modifier: Modifier = Modifier,
) {
val pullRefreshState = rememberPullToRefreshState()
val context = LocalContext.current
@@ -76,6 +83,7 @@ fun BeneficiaryListScreen(
MifosScaffold(
topBarTitleResId = R.string.beneficiaries,
navigateBack = navigateBack,
+ modifier = modifier,
floatingActionButtonContent = FloatingActionButtonContent(
onClick = addBeneficiaryClicked,
contentColor = MaterialTheme.colorScheme.onBackground,
@@ -83,16 +91,16 @@ fun BeneficiaryListScreen(
Icon(
painter = painterResource(id = R.drawable.ic_add_white_24dp),
contentDescription = null,
- tint = MaterialTheme.colorScheme.surfaceBright
+ tint = MaterialTheme.colorScheme.surfaceBright,
)
- }
- )
+ },
+ ),
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(it)
- .nestedScroll(pullRefreshState.nestedScrollConnection)
+ .nestedScroll(pullRefreshState.nestedScrollConnection),
) {
when (uiState) {
BeneficiaryListUiState.Loading -> {
@@ -104,21 +112,21 @@ fun BeneficiaryListScreen(
isNetworkConnected = Network.isConnected(context),
isRetryEnabled = true,
onRetry = retryLoadingBeneficiary,
- message = stringResource(R.string.error_fetching_beneficiaries)
+ message = stringResource(R.string.error_fetching_beneficiaries),
)
}
is BeneficiaryListUiState.Success -> {
- if(uiState.beneficiaries.isEmpty()) {
+ if (uiState.beneficiaries.isEmpty()) {
EmptyDataView(
modifier = Modifier.fillMaxSize(),
icon = R.drawable.ic_error_black_24dp,
- error = R.string.no_beneficiary_found_please_add
+ error = R.string.no_beneficiary_found_please_add,
)
} else {
ShowBeneficiary(
beneficiaryList = uiState.beneficiaries,
- onClick = onBeneficiaryItemClick
+ onClick = onBeneficiaryItemClick,
)
}
}
@@ -126,13 +134,12 @@ fun BeneficiaryListScreen(
PullToRefreshContainer(
state = pullRefreshState,
- modifier = Modifier.align(Alignment.TopCenter)
+ modifier = Modifier.align(Alignment.TopCenter),
)
}
LaunchedEffect(key1 = isRefreshing) {
- if (isRefreshing)
- pullRefreshState.startRefresh()
+ if (isRefreshing) pullRefreshState.startRefresh()
}
LaunchedEffect(key1 = pullRefreshState.isRefreshing) {
@@ -152,8 +159,9 @@ fun BeneficiaryListScreen(
}
}
-class BeneficiaryListScreenPreviewProvider : PreviewParameterProvider {
- val beneficiaryList = listOf(
+internal class BeneficiaryListScreenPreviewProvider :
+ PreviewParameterProvider {
+ private val beneficiaryList = listOf(
Beneficiary(
id = 982098302,
name = "John Doe",
@@ -161,7 +169,7 @@ class BeneficiaryListScreenPreviewProvider : PreviewParameterProvider
@@ -181,20 +189,21 @@ class BeneficiaryListScreenPreviewProvider : PreviewParameterProvider(
- BeneficiaryListUiState.Loading
- )
+ private val _beneficiaryListUiState = MutableStateFlow(Loading)
val beneficiaryListUiState: StateFlow get() = _beneficiaryListUiState
private val _isRefreshing = MutableStateFlow(false)
val isRefreshing: StateFlow get() = _isRefreshing.asStateFlow()
+ init {
+ loadBeneficiaries()
+ }
+
fun refresh() {
viewModelScope.launch {
_isRefreshing.emit(true)
@@ -33,10 +46,10 @@ class BeneficiaryListViewModel @Inject constructor(private val beneficiaryReposi
fun loadBeneficiaries() {
viewModelScope.launch {
- _beneficiaryListUiState.value = BeneficiaryListUiState.Loading
+ _beneficiaryListUiState.value = Loading
beneficiaryRepositoryImp.beneficiaryList().catch {
_beneficiaryListUiState.value = BeneficiaryListUiState.Error(it.message)
- }.collect { beneficiaryList->
+ }.collect { beneficiaryList ->
_beneficiaryListUiState.value = BeneficiaryListUiState.Success(beneficiaryList)
_isRefreshing.emit(false)
}
@@ -44,10 +57,8 @@ class BeneficiaryListViewModel @Inject constructor(private val beneficiaryReposi
}
}
-
-sealed class BeneficiaryListUiState{
+internal sealed class BeneficiaryListUiState {
data object Loading : BeneficiaryListUiState()
data class Error(val message: String?) : BeneficiaryListUiState()
data class Success(val beneficiaries: List) : BeneficiaryListUiState()
}
-
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/navigation/BeneficiaryNavGraph.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/navigation/BeneficiaryNavGraph.kt
index a8bda44ae..811ad0d18 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/navigation/BeneficiaryNavGraph.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/navigation/BeneficiaryNavGraph.kt
@@ -1,3 +1,12 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.beneficiary.navigation
import androidx.navigation.NavController
@@ -10,9 +19,9 @@ import androidx.navigation.navArgument
import org.mifos.mobile.core.common.Constants
import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
import org.mifos.mobile.core.model.enums.BeneficiaryState
-import org.mifos.mobile.feature.beneficiary.beneficiary_application.BeneficiaryApplicationScreen
-import org.mifos.mobile.feature.beneficiary.beneficiary_detail.BeneficiaryDetailScreen
-import org.mifos.mobile.feature.beneficiary.beneficiary_list.BeneficiaryListScreen
+import org.mifos.mobile.feature.beneficiary.beneficiaryApplication.BeneficiaryApplicationScreen
+import org.mifos.mobile.feature.beneficiary.beneficiaryDetail.BeneficiaryDetailScreen
+import org.mifos.mobile.feature.beneficiary.beneficiaryList.BeneficiaryListScreen
import org.mifos.mobile.feature.beneficiary.presentation.BeneficiaryScreen
fun NavController.navigateToBeneficiaryListScreen() {
@@ -23,23 +32,26 @@ fun NavController.navigateToAddBeneficiaryScreen() {
navigate(BeneficiaryNavigation.AddBeneficiary.route)
}
-fun NavController.navigateToBeneficiaryApplicationScreen(beneficiary: Beneficiary?, beneficiaryState: BeneficiaryState) {
+fun NavController.navigateToBeneficiaryApplicationScreen(
+ beneficiary: Beneficiary?,
+ beneficiaryState: BeneficiaryState,
+) {
navigate(
BeneficiaryNavigation.BeneficiaryApplication.passArguments(
beneficiaryId = beneficiary?.id ?: -1,
- beneficiaryState = beneficiaryState
- )
+ beneficiaryState = beneficiaryState,
+ ),
)
}
fun NavGraphBuilder.beneficiaryNavGraph(
navController: NavHostController,
openQrReaderScreen: () -> Unit,
- openQrImportScreen: () -> Unit
+ openQrImportScreen: () -> Unit,
) {
navigation(
startDestination = BeneficiaryNavigation.BeneficiaryList.route,
- route = BeneficiaryNavigation.BeneficiaryBaseRoute.route
+ route = BeneficiaryNavigation.BeneficiaryBaseRoute.route,
) {
beneficiaryListRoute(
navigateBack = navController::popBackStack,
@@ -47,28 +59,38 @@ fun NavGraphBuilder.beneficiaryNavGraph(
navController.navigate(BeneficiaryNavigation.AddBeneficiary.route)
},
showBeneficiaryDetail = { beneficiaryId ->
- navController.navigate(BeneficiaryNavigation.BeneficiaryDetail.passArguments(beneficiaryId = beneficiaryId))
- }
+ navController.navigate(
+ BeneficiaryNavigation.BeneficiaryDetail.passArguments(
+ beneficiaryId = beneficiaryId,
+ ),
+ )
+ },
)
addBeneficiaryRoute(
navigateBack = navController::popBackStack,
addBeneficiaryManually = {
navController.navigate(
- BeneficiaryNavigation.BeneficiaryApplication.passArguments(-1, BeneficiaryState.CREATE_MANUAL)
+ BeneficiaryNavigation.BeneficiaryApplication.passArguments(
+ -1,
+ BeneficiaryState.CREATE_MANUAL,
+ ),
)
},
openQrScanner = openQrReaderScreen,
- uploadQrCode = openQrImportScreen
+ uploadQrCode = openQrImportScreen,
)
beneficiaryDetailRoute(
navigateBack = navController::popBackStack,
updateBeneficiary = { beneficiary ->
navController.navigate(
- BeneficiaryNavigation.BeneficiaryApplication.passArguments(beneficiary?.id ?: -1, BeneficiaryState.UPDATE)
+ BeneficiaryNavigation.BeneficiaryApplication.passArguments(
+ beneficiary?.id ?: -1,
+ BeneficiaryState.UPDATE,
+ ),
)
- }
+ },
)
beneficiaryApplicationRoute(
@@ -80,13 +102,13 @@ fun NavGraphBuilder.beneficiaryNavGraph(
fun NavGraphBuilder.beneficiaryListRoute(
navigateBack: () -> Unit,
addBeneficiary: () -> Unit,
- showBeneficiaryDetail: (Int) -> Unit
+ showBeneficiaryDetail: (Int) -> Unit,
) {
composable(route = BeneficiaryNavigation.BeneficiaryList.route) {
BeneficiaryListScreen(
navigateBack = navigateBack,
addBeneficiaryClicked = addBeneficiary,
- onBeneficiaryItemClick = showBeneficiaryDetail
+ onBeneficiaryItemClick = showBeneficiaryDetail,
)
}
}
@@ -95,14 +117,14 @@ fun NavGraphBuilder.addBeneficiaryRoute(
navigateBack: () -> Unit,
addBeneficiaryManually: () -> Unit,
openQrScanner: () -> Unit,
- uploadQrCode: () -> Unit
+ uploadQrCode: () -> Unit,
) {
composable(route = BeneficiaryNavigation.AddBeneficiary.route) {
BeneficiaryScreen(
- topAppbarNavigateback = navigateBack,
- addiconClicked = addBeneficiaryManually,
- scaniconClicked = openQrScanner,
- uploadIconClicked = uploadQrCode
+ topAppbarNavigateBack = navigateBack,
+ addIconClicked = addBeneficiaryManually,
+ scanIconClicked = openQrScanner,
+ uploadIconClicked = uploadQrCode,
)
}
}
@@ -114,8 +136,8 @@ fun NavGraphBuilder.beneficiaryDetailRoute(
composable(
route = BeneficiaryNavigation.BeneficiaryDetail.route,
arguments = listOf(
- navArgument(name = BENEFICIARY_ID) { type = NavType.IntType }
- )
+ navArgument(name = BENEFICIARY_ID) { type = NavType.IntType },
+ ),
) {
BeneficiaryDetailScreen(
navigateBack = navigateBack,
@@ -131,11 +153,13 @@ fun NavGraphBuilder.beneficiaryApplicationRoute(
route = BeneficiaryNavigation.BeneficiaryApplication.route,
arguments = listOf(
navArgument(name = BENEFICIARY_ID) { type = NavType.IntType },
- navArgument(name = Constants.BENEFICIARY_STATE) { type = NavType.EnumType(BeneficiaryState::class.java) }
- )
+ navArgument(name = Constants.BENEFICIARY_STATE) {
+ type = NavType.EnumType(BeneficiaryState::class.java)
+ },
+ ),
) {
BeneficiaryApplicationScreen(
navigateBack = navigateBack,
)
}
-}
\ No newline at end of file
+}
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/navigation/BeneficiaryNavigationScreen.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/navigation/BeneficiaryNavigationScreen.kt
index 2131d7500..df22dcc2e 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/navigation/BeneficiaryNavigationScreen.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/navigation/BeneficiaryNavigationScreen.kt
@@ -1,10 +1,17 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.beneficiary.navigation
import org.mifos.mobile.core.common.Constants.BENEFICIARY_STATE
-import org.mifos.mobile.core.model.entity.beneficiary.Beneficiary
import org.mifos.mobile.core.model.enums.BeneficiaryState
-
const val BENEFICIARY_NAVIGATION_ROUTE = "beneficiary_route"
const val BENEFICIARY_LIST_ROUTE = "beneficiary_list_screen"
const val BENEFICIARY_DETAIL_SCREEN_ROUTE = "beneficiary_detail_screen"
@@ -19,11 +26,14 @@ sealed class BeneficiaryNavigation(val route: String) {
data object AddBeneficiary : BeneficiaryNavigation(route = ADD_BENEFICIARY_SCREEN_ROUTE)
- data object BeneficiaryDetail : BeneficiaryNavigation(route = "$BENEFICIARY_DETAIL_SCREEN_ROUTE/{$BENEFICIARY_ID}") {
- fun passArguments(beneficiaryId: Int) = "$BENEFICIARY_DETAIL_SCREEN_ROUTE/${beneficiaryId}"
+ data object BeneficiaryDetail :
+ BeneficiaryNavigation(route = "$BENEFICIARY_DETAIL_SCREEN_ROUTE/{$BENEFICIARY_ID}") {
+ fun passArguments(beneficiaryId: Int) = "$BENEFICIARY_DETAIL_SCREEN_ROUTE/$beneficiaryId"
}
- data object BeneficiaryApplication : BeneficiaryNavigation(route = "$BENEFICIARY_APPLICATION_SCREEN_ROUTE/{$BENEFICIARY_ID}/{$BENEFICIARY_STATE}") {
- fun passArguments(beneficiaryId: Int, beneficiaryState: BeneficiaryState) = "$BENEFICIARY_APPLICATION_SCREEN_ROUTE/${beneficiaryId}/${beneficiaryState}"
+ data object BeneficiaryApplication :
+ BeneficiaryNavigation(route = "$BENEFICIARY_APPLICATION_SCREEN_ROUTE/{$BENEFICIARY_ID}/{$BENEFICIARY_STATE}") {
+ fun passArguments(beneficiaryId: Int, beneficiaryState: BeneficiaryState) =
+ "$BENEFICIARY_APPLICATION_SCREEN_ROUTE/$beneficiaryId/$beneficiaryState"
}
-}
\ No newline at end of file
+}
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreen.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreen.kt
index 42dfcf005..42600d2b2 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreen.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreen.kt
@@ -1,8 +1,14 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.beneficiary.presentation
-import android.content.res.Configuration
-import android.os.Build
-import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
@@ -11,71 +17,64 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.mifos.mobile.core.designsystem.components.MifosTopBar
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.beneficiary.R
-
@Composable
-fun BeneficiaryScreen(
- topAppbarNavigateback: () -> Unit,
- addiconClicked: () -> Unit,
- scaniconClicked: () -> Unit,
+internal fun BeneficiaryScreen(
+ topAppbarNavigateBack: () -> Unit,
+ addIconClicked: () -> Unit,
+ scanIconClicked: () -> Unit,
uploadIconClicked: () -> Unit,
+ modifier: Modifier = Modifier,
) {
Scaffold(
topBar = {
MifosTopBar(
- navigateBack = topAppbarNavigateback,
+ navigateBack = topAppbarNavigateBack,
title = {
Text(text = stringResource(id = R.string.add_beneficiary))
- }
+ },
)
- }
+ },
+ modifier = modifier,
) {
Column(
modifier = Modifier
.padding(it)
- .padding(10.dp)
+ .padding(10.dp),
) {
Text(
stringResource(id = R.string.select_mode),
fontSize = 18.sp,
- color = MaterialTheme.colorScheme.onSurface
+ color = MaterialTheme.colorScheme.onSurface,
)
Text(
modifier = Modifier.padding(top = 16.dp),
text = stringResource(R.string.add_beneficiary_option),
fontSize = 18.sp,
- color = MaterialTheme.colorScheme.onSurface
+ color = MaterialTheme.colorScheme.onSurface,
)
BeneficiaryScreenIcons(
+ addIconClicked = addIconClicked,
+ scanIconClicked = scanIconClicked,
+ uploadIconClicked = uploadIconClicked,
modifier = Modifier.padding(top = 20.dp),
- addIconclicked = addiconClicked,
- scanIconClicked = scaniconClicked,
- uploadIconClicked = uploadIconClicked
)
}
}
}
-@RequiresApi(Build.VERSION_CODES.TIRAMISU)
-@Preview(
- name = "Night Mode",
- uiMode = Configuration.UI_MODE_NIGHT_YES,
-)
-@Preview(
- name = "Day mode",
- uiMode = Configuration.UI_MODE_NIGHT_NO,
-)
+@DevicePreviews
@Composable
-fun BeneficiaryScreenPreview() {
+private fun BeneficiaryScreenPreview() {
MifosMobileTheme {
BeneficiaryScreen({}, {}, {}, {})
}
-}
\ No newline at end of file
+}
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreenIcons.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreenIcons.kt
index 6f0b67524..354e4a8d6 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreenIcons.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/BeneficiaryScreenIcons.kt
@@ -1,84 +1,81 @@
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
package org.mifos.mobile.feature.beneficiary.presentation
-import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.beneficiary.R
-import org.mifos.mobile.ui.beneficiary.presentation.RenderIconAndText
@Composable
-fun BeneficiaryScreenIcons(
- modifier: Modifier = Modifier,
- addIconclicked: () -> Unit,
+internal fun BeneficiaryScreenIcons(
+ addIconClicked: () -> Unit,
scanIconClicked: () -> Unit,
- uploadIconClicked: () -> Unit
+ uploadIconClicked: () -> Unit,
+ modifier: Modifier = Modifier,
) {
-
Column(modifier = modifier) {
-
Row(
horizontalArrangement = Arrangement.SpaceAround,
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier.fillMaxWidth(),
) {
RenderIconAndText(
icon = R.drawable.ic_beneficiary_add_48px,
text = stringResource(id = R.string.add),
- icondescription = stringResource(id = R.string.add),
- iconClick = addIconclicked
+ iconDescription = stringResource(id = R.string.add),
+ iconClick = addIconClicked,
)
RenderIconAndText(
icon = R.drawable.ic_qrcode_scan_gray_dark,
text = stringResource(id = R.string.scan),
- icondescription = stringResource(id = R.string.scan),
- iconClick = scanIconClicked
+ iconDescription = stringResource(id = R.string.scan),
+ iconClick = scanIconClicked,
)
-
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
- modifier = Modifier.fillMaxWidth()
+ modifier = Modifier.fillMaxWidth(),
) {
RenderIconAndText(
icon = R.drawable.ic_file_upload_black_24dp,
text = stringResource(id = R.string.upload_qr_code),
- icondescription = stringResource(id = R.string.upload_qr_code),
- iconClick = uploadIconClicked
+ iconDescription = stringResource(id = R.string.upload_qr_code),
+ iconClick = uploadIconClicked,
)
-
}
}
}
-@Preview(
- name = "Night Mode",
- uiMode = Configuration.UI_MODE_NIGHT_YES,
-)
-@Preview(
- name = "Day mode",
- uiMode = Configuration.UI_MODE_NIGHT_NO,
-)
+@DevicePreviews
@Composable
-fun IconsScreenPreview() {
+private fun IconsScreenPreview() {
MifosMobileTheme {
-
- BeneficiaryScreenIcons(
- modifier = Modifier.padding(top = 20.dp),
- addIconclicked = {},
- scanIconClicked = {},
- uploadIconClicked = {}
- )
-
+ Surface {
+ BeneficiaryScreenIcons(
+ addIconClicked = {},
+ scanIconClicked = {},
+ uploadIconClicked = {},
+ modifier = Modifier.padding(top = 20.dp),
+ )
+ }
}
-}
\ No newline at end of file
+}
diff --git a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/RenderIconAndText.kt b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/RenderIconAndText.kt
index 394cef9bf..033b3bae3 100644
--- a/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/RenderIconAndText.kt
+++ b/feature/beneficiary/src/main/java/org/mifos/mobile/feature/beneficiary/presentation/RenderIconAndText.kt
@@ -1,6 +1,14 @@
-package org.mifos.mobile.ui.beneficiary.presentation
+/*
+ * Copyright 2024 Mifos Initiative
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
+ */
+package org.mifos.mobile.feature.beneficiary.presentation
-import android.content.res.Configuration
import androidx.annotation.DrawableRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@@ -8,72 +16,69 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
+import org.mifos.mobile.core.ui.utils.DevicePreviews
import org.mifos.mobile.feature.beneficiary.R
-
/**
* this is a reusable composable function that is made up of a text and icon composable
* some of the intake parameters are
* @param[icon]
- * @param[icondescription]
+ * @param[iconDescription]
* @param[text]
* @param[iconClick]
*
* */
@Composable
-fun RenderIconAndText(
+internal fun RenderIconAndText(
@DrawableRes icon: Int,
- icondescription: String,
text: String,
- iconClick: () -> Unit
+ iconDescription: String,
+ iconClick: () -> Unit,
+ modifier: Modifier = Modifier,
) {
-
- Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ Column(
+ modifier = modifier,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
Icon(
tint = MaterialTheme.colorScheme.onSurface,
painter = painterResource(id = icon),
- contentDescription = icondescription,
+ contentDescription = iconDescription,
modifier = Modifier
.height(85.dp)
.width(85.dp)
.clickable(
- onClick = iconClick
- )
+ onClick = iconClick,
+ ),
)
Text(
text,
- color = MaterialTheme.colorScheme.onSurface
+ color = MaterialTheme.colorScheme.onSurface,
)
}
}
-
-@Preview(
- name = "Night Mode",
- uiMode = Configuration.UI_MODE_NIGHT_YES,
-)
-@Preview(
- name = "Day mode",
- uiMode = Configuration.UI_MODE_NIGHT_NO,
-)
+@DevicePreviews
@Composable
-fun IconsAndTextPreview() {
+private fun IconsAndTextPreview() {
MifosMobileTheme {
- RenderIconAndText(
- icon = R.drawable.ic_qrcode_scan_gray_dark,
- text = stringResource(id = R.string.scan),
- icondescription = stringResource(id = R.string.scan),
- iconClick = {}
- )
+ Surface {
+ RenderIconAndText(
+ icon = R.drawable.ic_qrcode_scan_gray_dark,
+ text = stringResource(id = R.string.scan),
+ iconDescription = stringResource(id = R.string.scan),
+ iconClick = {},
+ )
+ }
}
-}
\ No newline at end of file
+}
diff --git a/feature/beneficiary/src/main/res/drawable/ic_add_white_24dp.xml b/feature/beneficiary/src/main/res/drawable/ic_add_white_24dp.xml
index d29480261..73c4dab96 100644
--- a/feature/beneficiary/src/main/res/drawable/ic_add_white_24dp.xml
+++ b/feature/beneficiary/src/main/res/drawable/ic_add_white_24dp.xml
@@ -1,3 +1,13 @@
+
+
+
+
+
+
+
Failed to fetch Beneficiaries
Failed to fetch Beneficiary Template
diff --git a/feature/beneficiary/src/test/java/org/mifos/mobile/feature/beneficiary/ExampleUnitTest.kt b/feature/beneficiary/src/test/java/org/mifos/mobile/feature/beneficiary/ExampleUnitTest.kt
deleted file mode 100644
index baf2b6123..000000000
--- a/feature/beneficiary/src/test/java/org/mifos/mobile/feature/beneficiary/ExampleUnitTest.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.mifos.mobile.feature.beneficiary
-
-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/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferContent.kt b/feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferContent.kt
index 2981b56d7..dd88357f6 100644
--- a/feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferContent.kt
+++ b/feature/third-party-transfer/src/main/java/org/mifos/mobile/feature/third/party/transfer/third_party_transfer/ThirdPartyTransferContent.kt
@@ -34,7 +34,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import org.mifos.mobile.core.designsystem.components.MifosOutlinedTextButton
import org.mifos.mobile.core.designsystem.components.MifosOutlinedTextField
-import org.mifos.mobile.core.designsystem.components.MifosTextButton
+import org.mifos.mobile.core.designsystem.components.MifosButton
import org.mifos.mobile.core.designsystem.theme.DarkGray
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
import org.mifos.mobile.core.designsystem.theme.Primary
@@ -222,7 +222,7 @@ fun BeneficiaryStep(
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.labelMedium
)
- MifosTextButton(
+ MifosButton(
onClick = { addBeneficiary() },
textResId = R.string.add_beneficiary
)
@@ -239,7 +239,7 @@ fun BeneficiaryStep(
beneficiaryError = false
}
)
- MifosTextButton(
+ MifosButton(
onClick = {
if (beneficiary == null) beneficiaryError = true
else onContinueClick(beneficiary!!)
@@ -294,7 +294,7 @@ fun EnterAmountStep(
supportingText = amountError?.let { stringResource(id = it) },
label = R.string.enter_amount,
)
- MifosTextButton(
+ MifosButton(
onClick = {
if (amountError == null) {
onContinueClick(amount.text)
@@ -352,7 +352,7 @@ fun RemarkStep(
)
Spacer(modifier = Modifier.height(12.dp))
Row {
- MifosTextButton(
+ MifosButton(
onClick = {
remarkError?.let { showRemarkError = true }
?: onContinueClicked(remark.text)
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 61916c74b..1fa3f380e 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -97,6 +97,7 @@ roborazzi = "1.6.0"
simplecropviewVersion = "1.1.8"
sweetErrorVersion = "1.0.0"
secrets = "2.0.1"
+slackComposeLint = "1.3.1"
tableviewVersion = "0.8.9.4"
targetSdk = "34"
@@ -219,6 +220,7 @@ libphonenumber-android = { module = "io.michaelrocks:libphonenumber-android", ve
lint-api = { group = "com.android.tools.lint", name = "lint-api", version.ref = "androidTools" }
lint-checks = { group = "com.android.tools.lint", name = "lint-checks", version.ref = "androidTools" }
lint-tests = { group = "com.android.tools.lint", name = "lint-tests", version.ref = "androidTools" }
+slack-compose-lint = {group="com.slack.lint.compose",name="compose-lint-checks",version.ref="slackComposeLint"}
#Detekt
detekt-formatting = { group = "io.gitlab.arturbosch.detekt", name = "detekt-formatting", version.ref = "detekt" }
diff --git a/lint/build.gradle.kts b/lint/build.gradle.kts
index 26b8acd38..964c67a53 100644
--- a/lint/build.gradle.kts
+++ b/lint/build.gradle.kts
@@ -29,8 +29,11 @@ kotlin {
}
dependencies {
+ lintChecks(libs.slack.compose.lint)
+
compileOnly(libs.kotlin.stdlib)
compileOnly(libs.lint.api)
+
testImplementation(libs.lint.checks)
testImplementation(libs.lint.tests)
testImplementation(kotlin("test"))