Compare commits
2 Commits
86b51fa99b
...
43477f9d47
| Author | SHA1 | Date | |
|---|---|---|---|
|
43477f9d47
|
|||
|
26c3b7418c
|
@@ -1,130 +0,0 @@
|
|||||||
package sh.sar.gridflow
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.view.GravityCompat
|
|
||||||
import androidx.drawerlayout.widget.DrawerLayout
|
|
||||||
import com.google.android.material.navigation.NavigationView
|
|
||||||
import sh.sar.gridflow.R
|
|
||||||
import sh.sar.gridflow.databinding.ActivityAddSubscriptionBinding
|
|
||||||
|
|
||||||
class AddSubscriptionActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
|
|
||||||
|
|
||||||
private lateinit var binding: ActivityAddSubscriptionBinding
|
|
||||||
private lateinit var drawerToggle: ActionBarDrawerToggle
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
binding = ActivityAddSubscriptionBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
setupViews()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupViews() {
|
|
||||||
// Setup toolbar with hamburger menu
|
|
||||||
setSupportActionBar(binding.toolbar)
|
|
||||||
supportActionBar?.apply {
|
|
||||||
title = "Add Subscription"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup drawer toggle
|
|
||||||
drawerToggle = ActionBarDrawerToggle(
|
|
||||||
this, binding.drawerLayout, binding.toolbar,
|
|
||||||
R.string.navigation_drawer_open, R.string.navigation_drawer_close
|
|
||||||
)
|
|
||||||
binding.drawerLayout.addDrawerListener(drawerToggle)
|
|
||||||
drawerToggle.syncState()
|
|
||||||
|
|
||||||
// Setup navigation
|
|
||||||
binding.navView.setNavigationItemSelectedListener(this)
|
|
||||||
|
|
||||||
// Handle continue button click
|
|
||||||
binding.btnContinue.setOnClickListener {
|
|
||||||
val subscriptionNumber = binding.editSubscriptionNumber.text.toString().trim()
|
|
||||||
val billNumber = binding.editBillNumber.text.toString().trim()
|
|
||||||
val alias = binding.editAlias.text.toString().trim()
|
|
||||||
|
|
||||||
if (validateInputs(subscriptionNumber, billNumber, alias)) {
|
|
||||||
Toast.makeText(this, "Continue action not implemented yet", Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun validateInputs(subscriptionNumber: String, billNumber: String, alias: String): Boolean {
|
|
||||||
var isValid = true
|
|
||||||
|
|
||||||
if (subscriptionNumber.isEmpty()) {
|
|
||||||
binding.editSubscriptionNumber.error = "Subscription number is required"
|
|
||||||
isValid = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (billNumber.isEmpty()) {
|
|
||||||
binding.editBillNumber.error = "Bill number is required"
|
|
||||||
isValid = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alias.isEmpty()) {
|
|
||||||
binding.editAlias.error = "Alias/Description is required"
|
|
||||||
isValid = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return isValid
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNavigationItemSelected(item: MenuItem): Boolean {
|
|
||||||
when (item.itemId) {
|
|
||||||
R.id.nav_dashboard -> {
|
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
||||||
intent.putExtra("navigate_to", "dashboard")
|
|
||||||
startActivity(intent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
R.id.nav_bill_history -> {
|
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
||||||
intent.putExtra("navigate_to", "bill_history")
|
|
||||||
startActivity(intent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
R.id.nav_subscriptions -> {
|
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
||||||
intent.putExtra("navigate_to", "subscriptions")
|
|
||||||
startActivity(intent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
R.id.nav_band_rates -> {
|
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
||||||
intent.putExtra("navigate_to", "band_rates")
|
|
||||||
startActivity(intent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
R.id.nav_pay_any_bill -> {
|
|
||||||
val intent = Intent(this, MainActivity::class.java)
|
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
|
||||||
intent.putExtra("navigate_to", "pay_any_bill")
|
|
||||||
startActivity(intent)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
|
||||||
override fun onBackPressed() {
|
|
||||||
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
|
|
||||||
binding.drawerLayout.closeDrawer(GravityCompat.START)
|
|
||||||
} else {
|
|
||||||
super.onBackPressed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -640,6 +640,110 @@ class FenakaApiService {
|
|||||||
val matchResult = regex.find(html)
|
val matchResult = regex.find(html)
|
||||||
return matchResult?.groupValues?.get(1)
|
return matchResult?.groupValues?.get(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun deleteCustomerSubscription(subscriptionId: Long, cookie: String): ApiResult<Unit> = withContext(Dispatchers.IO) {
|
||||||
|
Log.d(TAG, "Deleting customer subscription: $subscriptionId")
|
||||||
|
|
||||||
|
val cookieHeader = "connect.sid=$cookie"
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("$BASE_URL/api/customer-subscriptions/$subscriptionId")
|
||||||
|
.delete()
|
||||||
|
.header("Authorization", "Bearer $BEARER_TOKEN")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Cookie", cookieHeader)
|
||||||
|
.header("Host", "api.fenaka.mv")
|
||||||
|
.header("User-Agent", "Dart/3.5 (dart:io)")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
Log.d(TAG, "Delete subscription response code: ${response.code}")
|
||||||
|
|
||||||
|
when (response.code) {
|
||||||
|
200, 204 -> {
|
||||||
|
Log.d(TAG, "Subscription deleted successfully")
|
||||||
|
ApiResult.Success(Unit, null)
|
||||||
|
}
|
||||||
|
404 -> {
|
||||||
|
Log.d(TAG, "Subscription not found: ${response.code}")
|
||||||
|
ApiResult.Error("Subscription not found", response.code)
|
||||||
|
}
|
||||||
|
403 -> {
|
||||||
|
Log.d(TAG, "Access denied: ${response.code}")
|
||||||
|
ApiResult.Error("Access denied", response.code)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Log.d(TAG, "Failed to delete subscription: ${response.code}")
|
||||||
|
ApiResult.Error("Failed to delete subscription", response.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "Network error during subscription deletion", e)
|
||||||
|
ApiResult.Error("Network error: ${e.message}", -1)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unexpected error during subscription deletion", e)
|
||||||
|
ApiResult.Error("Unexpected error: ${e.message}", -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun addCustomerSubscription(name: String, subscriptionNumber: String, billNumber: String, cookie: String): ApiResult<Unit> = withContext(Dispatchers.IO) {
|
||||||
|
Log.d(TAG, "Adding customer subscription: $subscriptionNumber with name: $name")
|
||||||
|
|
||||||
|
val cookieHeader = "connect.sid=$cookie"
|
||||||
|
|
||||||
|
// Create request body
|
||||||
|
val requestBody = mapOf(
|
||||||
|
"name" to name,
|
||||||
|
"subscriptionNumber" to subscriptionNumber,
|
||||||
|
"billNumber" to billNumber
|
||||||
|
)
|
||||||
|
val jsonBody = gson.toJson(requestBody).toRequestBody(JSON_MEDIA_TYPE.toMediaType())
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url("$BASE_URL/api/customer-subscriptions")
|
||||||
|
.post(jsonBody)
|
||||||
|
.header("Authorization", "Bearer $BEARER_TOKEN")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Cookie", cookieHeader)
|
||||||
|
.header("Host", "api.fenaka.mv")
|
||||||
|
.header("User-Agent", "Dart/3.5 (dart:io)")
|
||||||
|
.build()
|
||||||
|
|
||||||
|
try {
|
||||||
|
val response = client.newCall(request).execute()
|
||||||
|
Log.d(TAG, "Add subscription response code: ${response.code}")
|
||||||
|
|
||||||
|
when (response.code) {
|
||||||
|
200, 201 -> {
|
||||||
|
Log.d(TAG, "Subscription added successfully")
|
||||||
|
ApiResult.Success(Unit, null)
|
||||||
|
}
|
||||||
|
400 -> {
|
||||||
|
Log.d(TAG, "Bad request: ${response.code}")
|
||||||
|
ApiResult.Error("Invalid subscription data", response.code)
|
||||||
|
}
|
||||||
|
409 -> {
|
||||||
|
Log.d(TAG, "Subscription already exists: ${response.code}")
|
||||||
|
ApiResult.Error("Subscription already exists", response.code)
|
||||||
|
}
|
||||||
|
403 -> {
|
||||||
|
Log.d(TAG, "Access denied: ${response.code}")
|
||||||
|
ApiResult.Error("Access denied", response.code)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
Log.d(TAG, "Failed to add subscription: ${response.code}")
|
||||||
|
ApiResult.Error("Failed to add subscription", response.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
Log.e(TAG, "Network error during subscription addition", e)
|
||||||
|
ApiResult.Error("Network error: ${e.message}", -1)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unexpected error during subscription addition", e)
|
||||||
|
ApiResult.Error("Unexpected error: ${e.message}", -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class ApiResult<T> {
|
sealed class ApiResult<T> {
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
package sh.sar.gridflow.ui.subscriptions
|
package sh.sar.gridflow.ui.subscriptions
|
||||||
|
|
||||||
import android.app.AlertDialog
|
import android.app.AlertDialog
|
||||||
import android.content.Intent
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import sh.sar.gridflow.AddSubscriptionActivity
|
import sh.sar.gridflow.data.BillLookupResponse
|
||||||
import sh.sar.gridflow.data.CustomerSubscription
|
import sh.sar.gridflow.data.CustomerSubscription
|
||||||
import sh.sar.gridflow.databinding.DialogDeleteSubscriptionBinding
|
import sh.sar.gridflow.databinding.DialogDeleteSubscriptionBinding
|
||||||
import sh.sar.gridflow.databinding.DialogEditSubscriptionBinding
|
import sh.sar.gridflow.databinding.DialogEditSubscriptionBinding
|
||||||
@@ -55,8 +58,51 @@ class SubscriptionsFragment : Fragment() {
|
|||||||
|
|
||||||
// Handle FAB click
|
// Handle FAB click
|
||||||
binding.fabAddSubscription.setOnClickListener {
|
binding.fabAddSubscription.setOnClickListener {
|
||||||
val intent = Intent(requireContext(), AddSubscriptionActivity::class.java)
|
showAddSubscriptionForm()
|
||||||
startActivity(intent)
|
}
|
||||||
|
|
||||||
|
// Handle cancel button click
|
||||||
|
binding.btnCancelAdd.setOnClickListener {
|
||||||
|
hideAddSubscriptionForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle continue button click
|
||||||
|
binding.btnContinue.setOnClickListener {
|
||||||
|
if (binding.btnContinue.text == "Add Subscription") {
|
||||||
|
// Card is showing, add the subscription
|
||||||
|
val alias = binding.editAlias.text.toString().trim()
|
||||||
|
val subscriptionNumber = binding.editSubscriptionNumber.text.toString().trim()
|
||||||
|
val billNumber = binding.editBillNumber.text.toString().trim()
|
||||||
|
|
||||||
|
if (alias.isNotEmpty()) {
|
||||||
|
subscriptionsViewModel.addSubscription(
|
||||||
|
name = alias,
|
||||||
|
subscriptionNumber = subscriptionNumber,
|
||||||
|
billNumber = billNumber,
|
||||||
|
onSuccess = {
|
||||||
|
Toast.makeText(requireContext(), "Subscription added successfully", Toast.LENGTH_SHORT).show()
|
||||||
|
hideAddSubscriptionForm()
|
||||||
|
},
|
||||||
|
onError = { errorMessage ->
|
||||||
|
Toast.makeText(requireContext(), "Failed to add subscription: $errorMessage", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
binding.editAlias.error = "Alias/Description is required"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Input fields are showing, do lookup
|
||||||
|
val subscriptionNumber = binding.editSubscriptionNumber.text.toString().trim()
|
||||||
|
val billNumber = binding.editBillNumber.text.toString().trim()
|
||||||
|
val alias = binding.editAlias.text.toString().trim()
|
||||||
|
|
||||||
|
if (validateInputs(subscriptionNumber, billNumber, alias)) {
|
||||||
|
// First, lookup the bill to get customer info
|
||||||
|
subscriptionsViewModel.lookupBill(billNumber, subscriptionNumber) { errorMessage ->
|
||||||
|
Toast.makeText(requireContext(), "Failed to find subscription: $errorMessage", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle retry button
|
// Handle retry button
|
||||||
@@ -91,6 +137,24 @@ class SubscriptionsFragment : Fragment() {
|
|||||||
showErrorState(error)
|
showErrorState(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bill lookup result
|
||||||
|
subscriptionsViewModel.billLookupResult.observe(viewLifecycleOwner) { billLookup ->
|
||||||
|
if (billLookup != null) {
|
||||||
|
showCustomerInfo(billLookup)
|
||||||
|
} else {
|
||||||
|
hideCustomerInfo()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup loading state
|
||||||
|
subscriptionsViewModel.isLookingUp.observe(viewLifecycleOwner) { isLookingUp ->
|
||||||
|
if (isLookingUp) {
|
||||||
|
showLookupLoading()
|
||||||
|
} else {
|
||||||
|
hideLookupLoading()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showLoadingState() {
|
private fun showLoadingState() {
|
||||||
@@ -131,14 +195,48 @@ class SubscriptionsFragment : Fragment() {
|
|||||||
.create()
|
.create()
|
||||||
|
|
||||||
dialogBinding.btnCancel.setOnClickListener {
|
dialogBinding.btnCancel.setOnClickListener {
|
||||||
|
hideKeyboard()
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogBinding.btnSubmit.setOnClickListener {
|
dialogBinding.btnSubmit.setOnClickListener {
|
||||||
val newName = dialogBinding.editSubscriptionName.text.toString().trim()
|
val newName = dialogBinding.editSubscriptionName.text.toString().trim()
|
||||||
if (newName.isNotEmpty()) {
|
if (newName.isNotEmpty()) {
|
||||||
Toast.makeText(context, "Edit API call needed for: $newName", Toast.LENGTH_SHORT).show()
|
hideKeyboard()
|
||||||
dialog.dismiss()
|
|
||||||
|
// Get bill number from subscription data - try lastBill first, then fallback
|
||||||
|
val billNumber = subscription.subscription.lastBill?.billNumber ?: ""
|
||||||
|
val subscriptionNumber = subscription.subscription.subscriptionNumber
|
||||||
|
|
||||||
|
if (billNumber.isEmpty()) {
|
||||||
|
Toast.makeText(context, "Cannot edit: Bill number not available", Toast.LENGTH_SHORT).show()
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edit = Delete + Add (since there's no edit API)
|
||||||
|
subscriptionsViewModel.deleteSubscription(
|
||||||
|
subscriptionId = subscription.id,
|
||||||
|
onSuccess = {
|
||||||
|
// After successful delete, add the subscription with new name
|
||||||
|
subscriptionsViewModel.addSubscription(
|
||||||
|
name = newName,
|
||||||
|
subscriptionNumber = subscriptionNumber,
|
||||||
|
billNumber = billNumber,
|
||||||
|
onSuccess = {
|
||||||
|
Toast.makeText(requireContext(), "Subscription updated successfully", Toast.LENGTH_SHORT).show()
|
||||||
|
dialog.dismiss()
|
||||||
|
},
|
||||||
|
onError = { errorMessage ->
|
||||||
|
Toast.makeText(requireContext(), "Failed to update subscription: $errorMessage", Toast.LENGTH_SHORT).show()
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onError = { errorMessage ->
|
||||||
|
Toast.makeText(requireContext(), "Failed to update subscription: $errorMessage", Toast.LENGTH_SHORT).show()
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
dialogBinding.editSubscriptionName.error = "Name cannot be empty"
|
dialogBinding.editSubscriptionName.error = "Name cannot be empty"
|
||||||
}
|
}
|
||||||
@@ -159,13 +257,139 @@ class SubscriptionsFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dialogBinding.btnYes.setOnClickListener {
|
dialogBinding.btnYes.setOnClickListener {
|
||||||
Toast.makeText(context, "Delete API call needed for: ${subscription.name}", Toast.LENGTH_SHORT).show()
|
subscriptionsViewModel.deleteSubscription(
|
||||||
dialog.dismiss()
|
subscriptionId = subscription.id,
|
||||||
|
onSuccess = {
|
||||||
|
Toast.makeText(context, "Subscription deleted successfully", Toast.LENGTH_SHORT).show()
|
||||||
|
dialog.dismiss()
|
||||||
|
},
|
||||||
|
onError = { errorMessage ->
|
||||||
|
Toast.makeText(context, "Failed to delete subscription: $errorMessage", Toast.LENGTH_SHORT).show()
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
dialog.show()
|
dialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showAddSubscriptionForm() {
|
||||||
|
binding.scrollAddForm.visibility = View.VISIBLE
|
||||||
|
binding.recyclerSubscriptions.visibility = View.GONE
|
||||||
|
binding.layoutError.visibility = View.GONE
|
||||||
|
binding.layoutEmpty.visibility = View.GONE
|
||||||
|
binding.progressLoading.visibility = View.GONE
|
||||||
|
binding.fabAddSubscription.visibility = View.GONE
|
||||||
|
|
||||||
|
// Clear form fields and customer info
|
||||||
|
binding.editSubscriptionNumber.text?.clear()
|
||||||
|
binding.editBillNumber.text?.clear()
|
||||||
|
binding.editAlias.text?.clear()
|
||||||
|
subscriptionsViewModel.clearBillLookup()
|
||||||
|
|
||||||
|
// Ensure initial state is correct
|
||||||
|
binding.inputFieldsLayout.visibility = View.VISIBLE
|
||||||
|
binding.cardCustomerInfo.visibility = View.GONE
|
||||||
|
binding.btnContinue.text = "Continue"
|
||||||
|
binding.btnContinue.visibility = View.VISIBLE
|
||||||
|
binding.btnCancelAdd.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideAddSubscriptionForm() {
|
||||||
|
hideKeyboard()
|
||||||
|
binding.scrollAddForm.visibility = View.GONE
|
||||||
|
binding.fabAddSubscription.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
// Show appropriate state based on subscriptions
|
||||||
|
subscriptionsViewModel.subscriptions.value?.let { subscriptions ->
|
||||||
|
if (subscriptions.isEmpty()) {
|
||||||
|
showEmptyState()
|
||||||
|
} else {
|
||||||
|
showSubscriptionsList()
|
||||||
|
}
|
||||||
|
} ?: showEmptyState()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateInputs(subscriptionNumber: String, billNumber: String, alias: String): Boolean {
|
||||||
|
var isValid = true
|
||||||
|
|
||||||
|
if (subscriptionNumber.isEmpty()) {
|
||||||
|
binding.editSubscriptionNumber.error = "Subscription number is required"
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (billNumber.isEmpty()) {
|
||||||
|
binding.editBillNumber.error = "Bill number is required"
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alias.isEmpty()) {
|
||||||
|
binding.editAlias.error = "Alias/Description is required"
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showCustomerInfo(billLookup: BillLookupResponse) {
|
||||||
|
// Hide keyboard when showing customer info
|
||||||
|
hideKeyboard()
|
||||||
|
|
||||||
|
// Hide input fields and show card
|
||||||
|
binding.inputFieldsLayout.visibility = View.GONE
|
||||||
|
binding.cardCustomerInfo.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
// Change button text to "Add Subscription"
|
||||||
|
binding.btnContinue.text = "Add Subscription"
|
||||||
|
|
||||||
|
// Service type
|
||||||
|
val serviceType = when (billLookup.subscription.serviceId) {
|
||||||
|
1 -> "Electricity"
|
||||||
|
2 -> "Water"
|
||||||
|
else -> "Unknown Service"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Customer information
|
||||||
|
binding.tvCustomerName.text = billLookup.customer.name
|
||||||
|
binding.tvPhoneNumber.text = billLookup.customer.phone
|
||||||
|
|
||||||
|
// Address information
|
||||||
|
val propertyName = billLookup.subscriptionAddress?.property?.name ?: "Unknown Property"
|
||||||
|
val streetName = billLookup.subscriptionAddress?.property?.street?.name ?: "Unknown Street"
|
||||||
|
binding.tvAddress.text = "$propertyName, $streetName"
|
||||||
|
|
||||||
|
// Service details
|
||||||
|
binding.tvSubscriptionNumber.text = billLookup.subscription.subscriptionNumber
|
||||||
|
binding.tvServiceType.text = serviceType
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideCustomerInfo() {
|
||||||
|
binding.cardCustomerInfo.visibility = View.GONE
|
||||||
|
binding.inputFieldsLayout.visibility = View.VISIBLE
|
||||||
|
binding.btnContinue.text = "Continue"
|
||||||
|
|
||||||
|
// Ensure buttons are visible
|
||||||
|
binding.btnContinue.visibility = View.VISIBLE
|
||||||
|
binding.btnCancelAdd.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showLookupLoading() {
|
||||||
|
binding.layoutLookupLoading.visibility = View.VISIBLE
|
||||||
|
binding.cardCustomerInfo.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideLookupLoading() {
|
||||||
|
binding.layoutLookupLoading.visibility = View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideKeyboard() {
|
||||||
|
val inputMethodManager = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
val currentFocusView = activity?.currentFocus
|
||||||
|
if (currentFocusView != null) {
|
||||||
|
inputMethodManager.hideSoftInputFromWindow(currentFocusView.windowToken, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
_binding = null
|
_binding = null
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import androidx.lifecycle.LiveData
|
|||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import sh.sar.gridflow.data.BillLookupResponse
|
||||||
import sh.sar.gridflow.data.CustomerSubscription
|
import sh.sar.gridflow.data.CustomerSubscription
|
||||||
import sh.sar.gridflow.network.ApiResult
|
import sh.sar.gridflow.network.ApiResult
|
||||||
import sh.sar.gridflow.network.FenakaApiService
|
import sh.sar.gridflow.network.FenakaApiService
|
||||||
@@ -30,6 +31,12 @@ class SubscriptionsViewModel(application: Application) : AndroidViewModel(applic
|
|||||||
private val _error = MutableLiveData<String?>()
|
private val _error = MutableLiveData<String?>()
|
||||||
val error: LiveData<String?> = _error
|
val error: LiveData<String?> = _error
|
||||||
|
|
||||||
|
private val _billLookupResult = MutableLiveData<BillLookupResponse?>()
|
||||||
|
val billLookupResult: LiveData<BillLookupResponse?> = _billLookupResult
|
||||||
|
|
||||||
|
private val _isLookingUp = MutableLiveData<Boolean>()
|
||||||
|
val isLookingUp: LiveData<Boolean> = _isLookingUp
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadSubscriptions()
|
loadSubscriptions()
|
||||||
}
|
}
|
||||||
@@ -66,4 +73,95 @@ class SubscriptionsViewModel(application: Application) : AndroidViewModel(applic
|
|||||||
Log.d(TAG, "Refreshing subscriptions")
|
Log.d(TAG, "Refreshing subscriptions")
|
||||||
loadSubscriptions()
|
loadSubscriptions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteSubscription(subscriptionId: Long, onSuccess: () -> Unit, onError: (String) -> Unit) {
|
||||||
|
val cookie = secureStorage.getCookie()
|
||||||
|
if (cookie == null) {
|
||||||
|
Log.d(TAG, "No cookie found, cannot delete subscription")
|
||||||
|
onError("Authentication required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
Log.d(TAG, "Deleting subscription: $subscriptionId")
|
||||||
|
when (val result = apiService.deleteCustomerSubscription(subscriptionId, cookie)) {
|
||||||
|
is ApiResult.Success -> {
|
||||||
|
Log.d(TAG, "Subscription deleted successfully")
|
||||||
|
loadSubscriptions() // Refresh the list
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
|
is ApiResult.Error -> {
|
||||||
|
Log.d(TAG, "Failed to delete subscription: ${result.message}")
|
||||||
|
onError(result.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun lookupBill(billNumber: String, subscriptionNumber: String, onError: (String) -> Unit) {
|
||||||
|
_isLookingUp.value = true
|
||||||
|
_billLookupResult.value = null
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
Log.d(TAG, "Looking up bill: $billNumber, subscription: $subscriptionNumber")
|
||||||
|
when (val result = apiService.findBill(billNumber, subscriptionNumber)) {
|
||||||
|
is ApiResult.Success -> {
|
||||||
|
Log.d(TAG, "Bill lookup successful: ${result.data.customer.name}")
|
||||||
|
_isLookingUp.value = false
|
||||||
|
_billLookupResult.value = result.data
|
||||||
|
Log.d(TAG, "BillLookupResult LiveData updated")
|
||||||
|
}
|
||||||
|
is ApiResult.Error -> {
|
||||||
|
if (result.code == 404) {
|
||||||
|
Log.d(TAG, "404 received, trying with swapped parameters")
|
||||||
|
// Try again with swapped parameters
|
||||||
|
when (val retryResult = apiService.findBill(subscriptionNumber, billNumber)) {
|
||||||
|
is ApiResult.Success -> {
|
||||||
|
Log.d(TAG, "Retry successful with swapped parameters")
|
||||||
|
_isLookingUp.value = false
|
||||||
|
_billLookupResult.value = retryResult.data
|
||||||
|
}
|
||||||
|
is ApiResult.Error -> {
|
||||||
|
Log.d(TAG, "Retry also failed: ${retryResult.message}")
|
||||||
|
_isLookingUp.value = false
|
||||||
|
onError(retryResult.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, "Bill lookup failed: ${result.message}")
|
||||||
|
_isLookingUp.value = false
|
||||||
|
onError(result.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearBillLookup() {
|
||||||
|
_billLookupResult.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addSubscription(name: String, subscriptionNumber: String, billNumber: String, onSuccess: () -> Unit, onError: (String) -> Unit) {
|
||||||
|
val cookie = secureStorage.getCookie()
|
||||||
|
if (cookie == null) {
|
||||||
|
Log.d(TAG, "No cookie found, cannot add subscription")
|
||||||
|
onError("Authentication required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModelScope.launch {
|
||||||
|
Log.d(TAG, "Adding subscription: $subscriptionNumber with name: $name")
|
||||||
|
when (val result = apiService.addCustomerSubscription(name, subscriptionNumber, billNumber, cookie)) {
|
||||||
|
is ApiResult.Success -> {
|
||||||
|
Log.d(TAG, "Subscription added successfully")
|
||||||
|
loadSubscriptions() // Refresh the list
|
||||||
|
onSuccess()
|
||||||
|
}
|
||||||
|
is ApiResult.Error -> {
|
||||||
|
Log.d(TAG, "Failed to add subscription: ${result.message}")
|
||||||
|
onError(result.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,178 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/drawer_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
tools:context=".AddSubscriptionActivity">
|
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="?android:attr/colorBackground">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:theme="@style/Theme.GridFlow.AppBarOverlay">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
android:background="?attr/colorPrimary" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:fillViewport="true"
|
|
||||||
android:layout_marginTop="?attr/actionBarSize">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="32dp"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<!-- Logo and Title Section -->
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:gravity="center">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/iv_app_logo"
|
|
||||||
android:layout_width="120dp"
|
|
||||||
android:layout_height="120dp"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:src="@mipmap/ic_launcher"
|
|
||||||
android:contentDescription="GridFlow Logo" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Add New Subscription"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
|
||||||
android:layout_marginBottom="8dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Enter your subscription details below"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textColor="?android:attr/textColorSecondary"
|
|
||||||
android:alpha="0.7"
|
|
||||||
android:layout_marginBottom="32dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Subscription Form -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/input_form_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_marginTop="32dp">
|
|
||||||
|
|
||||||
<!-- Input Fields Container -->
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/input_fields_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<!-- Subscription Number Input -->
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:hint="Subscription Number"
|
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/edit_subscription_number"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="text"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<!-- Bill Number Input -->
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:hint="Bill Number"
|
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/edit_bill_number"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="text"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
<!-- Alias/Description Input -->
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:hint="Alias/Description"
|
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputEditText
|
|
||||||
android:id="@+id/edit_alias"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:inputType="text"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Continue Button -->
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btn_continue"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:text="Continue"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:layout_marginBottom="24dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<!-- Spacer for bottom -->
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="32dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.navigation.NavigationView
|
|
||||||
android:id="@+id/nav_view"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="start"
|
|
||||||
android:fitsSystemWindows="true"
|
|
||||||
app:headerLayout="@layout/nav_header_main"
|
|
||||||
app:menu="@menu/activity_main_drawer" />
|
|
||||||
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
|
||||||
@@ -82,6 +82,319 @@
|
|||||||
android:paddingBottom="80dp"
|
android:paddingBottom="80dp"
|
||||||
tools:listitem="@layout/item_subscription_detailed" />
|
tools:listitem="@layout/item_subscription_detailed" />
|
||||||
|
|
||||||
|
<!-- Add subscription form -->
|
||||||
|
<ScrollView
|
||||||
|
android:id="@+id/scroll_add_form"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fillViewport="true"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:background="?android:attr/colorBackground">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="32dp"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<!-- Header Section -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Add New Subscription"
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Enter your subscription details below"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:alpha="0.7" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Form Fields -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/input_fields_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<!-- Subscription Number Input -->
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Subscription Number"
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/edit_subscription_number"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<!-- Bill Number Input -->
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Bill Number"
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/edit_bill_number"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<!-- Alias/Description Input -->
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:hint="Alias/Description"
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/edit_alias"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Subscription Details Card -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/card_customer_info"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="?android:attr/colorBackground"
|
||||||
|
android:elevation="4dp"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<!-- Customer Details -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Customer Details"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="4dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Name:"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_customer_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="4dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Phone:"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_phone_number"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Address:"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_address"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Service Details -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Service Details"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="4dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Subscription:"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_subscription_number"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Service:"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_service_type"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Loading indicator for lookup -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layout_lookup_loading"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:visibility="gone">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="8dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Looking up subscription details..."
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textColor="?android:attr/textColorSecondary" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Buttons -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="end">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_cancel_add"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:text="Cancel"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
style="@style/Widget.MaterialComponents.Button.TextButton" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btn_continue"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:text="Continue"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:paddingHorizontal="24dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fab_add_subscription"
|
android:id="@+id/fab_add_subscription"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
Reference in New Issue
Block a user