fixed staff accounts and added coupons andriod
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
package com.example.petstoremobile.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.dtos.CouponDTO;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CouponAdapter extends RecyclerView.Adapter<CouponAdapter.ViewHolder> {
|
||||
private final List<CouponDTO> coupons;
|
||||
private final OnCouponClickListener listener;
|
||||
private boolean selectionMode = false;
|
||||
private final Set<Long> selectedIds = new HashSet<>();
|
||||
|
||||
public interface OnCouponClickListener {
|
||||
void onCouponClick(CouponDTO coupon);
|
||||
void onSelectionChanged(int count);
|
||||
}
|
||||
|
||||
public CouponAdapter(List<CouponDTO> coupons, OnCouponClickListener listener) {
|
||||
this.coupons = coupons;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_coupon, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
CouponDTO coupon = coupons.get(position);
|
||||
holder.tvCouponCode.setText(coupon.getCouponCode());
|
||||
|
||||
String discountText = "";
|
||||
if ("PERCENT".equals(coupon.getDiscountType())) {
|
||||
discountText = coupon.getDiscountValue().stripTrailingZeros().toPlainString() + "% OFF";
|
||||
} else {
|
||||
discountText = "$" + coupon.getDiscountValue().stripTrailingZeros().toPlainString() + " OFF";
|
||||
}
|
||||
holder.tvCouponDiscount.setText(discountText);
|
||||
|
||||
holder.tvCouponMinOrder.setText("Min order: $" + coupon.getMinOrderAmount().stripTrailingZeros().toPlainString());
|
||||
|
||||
if (coupon.getEndsAt() != null) {
|
||||
holder.tvCouponExpiry.setText("Expires: " + coupon.getEndsAt().substring(0, 10));
|
||||
holder.tvCouponExpiry.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.tvCouponExpiry.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (Boolean.TRUE.equals(coupon.getActive())) {
|
||||
holder.tvCouponStatus.setText("ACTIVE");
|
||||
holder.tvCouponStatus.setBackgroundTintList(ContextCompat.getColorStateList(holder.itemView.getContext(), R.color.primary_dark));
|
||||
} else {
|
||||
holder.tvCouponStatus.setText("INACTIVE");
|
||||
holder.tvCouponStatus.setBackgroundTintList(ContextCompat.getColorStateList(holder.itemView.getContext(), R.color.accent_coral));
|
||||
}
|
||||
|
||||
holder.cbSelectCoupon.setVisibility(selectionMode ? View.VISIBLE : View.GONE);
|
||||
holder.cbSelectCoupon.setChecked(selectedIds.contains(coupon.getCouponId()));
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (selectionMode) {
|
||||
toggleSelection(coupon.getCouponId());
|
||||
} else {
|
||||
listener.onCouponClick(coupon);
|
||||
}
|
||||
});
|
||||
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
if (!selectionMode) {
|
||||
setSelectionMode(true);
|
||||
toggleSelection(coupon.getCouponId());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
holder.cbSelectCoupon.setOnClickListener(v -> toggleSelection(coupon.getCouponId()));
|
||||
}
|
||||
|
||||
private void toggleSelection(Long id) {
|
||||
if (selectedIds.contains(id)) {
|
||||
selectedIds.remove(id);
|
||||
} else {
|
||||
selectedIds.add(id);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
listener.onSelectionChanged(selectedIds.size());
|
||||
}
|
||||
|
||||
public void setSelectionMode(boolean selectionMode) {
|
||||
this.selectionMode = selectionMode;
|
||||
if (!selectionMode) selectedIds.clear();
|
||||
notifyDataSetChanged();
|
||||
listener.onSelectionChanged(selectedIds.size());
|
||||
}
|
||||
|
||||
public Set<Long> getSelectedIds() {
|
||||
return selectedIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return coupons.size();
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvCouponCode, tvCouponDiscount, tvCouponMinOrder, tvCouponExpiry, tvCouponStatus;
|
||||
CheckBox cbSelectCoupon;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvCouponCode = itemView.findViewById(R.id.tvCouponCode);
|
||||
tvCouponDiscount = itemView.findViewById(R.id.tvCouponDiscount);
|
||||
tvCouponMinOrder = itemView.findViewById(R.id.tvCouponMinOrder);
|
||||
tvCouponExpiry = itemView.findViewById(R.id.tvCouponExpiry);
|
||||
tvCouponStatus = itemView.findViewById(R.id.tvCouponStatus);
|
||||
cbSelectCoupon = itemView.findViewById(R.id.cbSelectCoupon);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,14 +7,19 @@ import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.api.UserApi;
|
||||
import com.example.petstoremobile.databinding.ItemEmployeeBinding;
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.utils.GlideUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.EmployeeViewHolder> {
|
||||
|
||||
private List<EmployeeDTO> list;
|
||||
private OnEmployeeClickListener listener;
|
||||
private String baseUrl;
|
||||
private String token;
|
||||
|
||||
public interface OnEmployeeClickListener {
|
||||
void onEmployeeClick(int position);
|
||||
@@ -25,6 +30,14 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public static class EmployeeViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemEmployeeBinding binding;
|
||||
|
||||
@@ -66,8 +79,13 @@ public class EmployeeAdapter extends RecyclerView.Adapter<EmployeeAdapter.Employ
|
||||
binding.tvEmployeeStatus.setText(active ? "Active" : "Inactive");
|
||||
binding.tvEmployeeStatus.setTextColor(active ? Color.parseColor("#4CAF50") : Color.parseColor("#F44336"));
|
||||
|
||||
// Placeholder for profile image - matching Pet style
|
||||
binding.ivEmployeeProfile.setImageResource(R.drawable.placeholder);
|
||||
// Profile image
|
||||
if (baseUrl != null && e.getId() != null) {
|
||||
String imageUrl = baseUrl + String.format(UserApi.AVATAR_PATH, e.getId());
|
||||
GlideUtils.loadImageWithTokenCircle(holder.itemView.getContext(), binding.ivEmployeeProfile, imageUrl, token, R.drawable.placeholder);
|
||||
} else {
|
||||
binding.ivEmployeeProfile.setImageResource(R.drawable.placeholder);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> listener.onEmployeeClick(position));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import com.example.petstoremobile.dtos.CouponDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.DELETE;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
public interface CouponApi {
|
||||
|
||||
@GET("api/v1/coupons")
|
||||
Call<PageResponse<CouponDTO>> getAllCoupons(
|
||||
@Query("page") int page,
|
||||
@Query("size") int size,
|
||||
@Query("active") Boolean active,
|
||||
@Query("discountType") String discountType,
|
||||
@Query("sort") String sort);
|
||||
|
||||
@GET("api/v1/coupons/{id}")
|
||||
Call<CouponDTO> getCouponById(@Path("id") Long id);
|
||||
|
||||
@GET("api/v1/coupons/code/{code}")
|
||||
Call<CouponDTO> getCouponByCode(@Path("code") String code);
|
||||
|
||||
@POST("api/v1/coupons")
|
||||
Call<CouponDTO> createCoupon(@Body CouponDTO coupon);
|
||||
|
||||
@PUT("api/v1/coupons/{id}")
|
||||
Call<CouponDTO> updateCoupon(@Path("id") Long id, @Body CouponDTO coupon);
|
||||
|
||||
@DELETE("api/v1/coupons/{id}")
|
||||
Call<Void> deleteCoupon(@Path("id") Long id);
|
||||
|
||||
@DELETE("api/v1/coupons")
|
||||
Call<Void> bulkDeleteCoupons(@Query("ids") List<Long> ids);
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
package com.example.petstoremobile.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.example.petstoremobile.BuildConfig;
|
||||
import com.example.petstoremobile.api.auth.AuthApi;
|
||||
import com.example.petstoremobile.api.auth.AuthInterceptor;
|
||||
import com.example.petstoremobile.api.auth.TokenManager;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
//Retrofit client Used for API calls TODO: DELETE THIS FILE AFTER MERGE NOW THAT WE ARE USING HILT AND NETWORKMODULE
|
||||
public class RetrofitClient {
|
||||
private static final String TAG = "RetrofitClient";
|
||||
public static final String BASE_URL = getBaseUrl();
|
||||
|
||||
// Helper function to determine BASE_URL based on whether we are testing on an emulator or a real device
|
||||
private static String getBaseUrl() {
|
||||
String url = isEmulator() ? BuildConfig.EMULATOR_BACKEND_URL : BuildConfig.DEVICE_BACKEND_URL;
|
||||
Log.i(TAG, "Using backend URL: " + url + " (emulator=" + isEmulator() + ")");
|
||||
return url;
|
||||
}
|
||||
|
||||
private static boolean isEmulator() {
|
||||
return Build.FINGERPRINT.startsWith("generic")
|
||||
|| Build.FINGERPRINT.startsWith("unknown")
|
||||
|| Build.MODEL.contains("google_sdk")
|
||||
|| Build.MODEL.contains("Emulator")
|
||||
|| Build.MODEL.contains("Android SDK built for x86")
|
||||
|| Build.MANUFACTURER.contains("Genymotion")
|
||||
|| Build.HARDWARE.contains("goldfish")
|
||||
|| Build.HARDWARE.contains("ranchu")
|
||||
|| Build.PRODUCT.contains("sdk")
|
||||
|| Build.PRODUCT.contains("sdk_gphone")
|
||||
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"));
|
||||
}
|
||||
|
||||
private static Retrofit retrofit = null;
|
||||
|
||||
public static Retrofit getClient(Context context) {
|
||||
//create an http logging using an interceptor
|
||||
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
|
||||
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.addInterceptor(interceptor)
|
||||
.addInterceptor(new AuthInterceptor(new TokenManager(context)))
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
//build the retrofit object with all needed properties
|
||||
retrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.addConverterFactory(GsonConverterFactory.create()) //JSON converter
|
||||
.client(client) //logging interceptor - OkHttpClient
|
||||
.build();
|
||||
|
||||
return retrofit;
|
||||
}
|
||||
|
||||
//associate the retrofit object with the API interface
|
||||
public static PetApi getPetApi(Context context) {
|
||||
return getClient(context).create(PetApi.class);
|
||||
}
|
||||
|
||||
public static ServiceApi getServiceApi(Context context) {
|
||||
return getClient(context).create(ServiceApi.class);
|
||||
}
|
||||
|
||||
public static SupplierApi getSupplierApi(Context context) {
|
||||
return getClient(context).create(SupplierApi.class);
|
||||
}
|
||||
|
||||
public static AdoptionApi getAdoptionApi(Context context) {
|
||||
return getClient(context).create(AdoptionApi.class);
|
||||
}
|
||||
|
||||
public static AppointmentApi getAppointmentApi(Context context) {
|
||||
return getClient(context).create(AppointmentApi.class);
|
||||
}
|
||||
|
||||
public static ProductApi getProductApi(Context context) {
|
||||
return getClient(context).create(ProductApi.class);
|
||||
}
|
||||
|
||||
public static SaleApi getSaleApi(Context context) {
|
||||
return getClient(context).create(SaleApi.class);
|
||||
}
|
||||
|
||||
public static PurchaseOrderApi getPurchaseOrderApi(Context context) {
|
||||
return getClient(context).create(PurchaseOrderApi.class);
|
||||
}
|
||||
|
||||
public static ProductSupplierApi getProductSupplierApi(Context context) {
|
||||
return getClient(context).create(ProductSupplierApi.class);
|
||||
}
|
||||
|
||||
public static InventoryApi getInventoryApi(Context context) {
|
||||
return getClient(context).create(InventoryApi.class);
|
||||
}
|
||||
|
||||
public static AuthApi getAuthApi(Context context) {
|
||||
return getClient(context).create(AuthApi.class);
|
||||
}
|
||||
|
||||
public static ChatApi getChatApi(Context context) {
|
||||
return getClient(context).create(ChatApi.class);
|
||||
}
|
||||
|
||||
public static CustomerApi getCustomerApi(Context context) {
|
||||
return getClient(context).create(CustomerApi.class);
|
||||
}
|
||||
|
||||
public static MessageApi getMessageApi(Context context) {
|
||||
return getClient(context).create(MessageApi.class);
|
||||
}
|
||||
|
||||
public static StoreApi getStoreApi(Context context) {
|
||||
return getClient(context).create(StoreApi.class);
|
||||
}
|
||||
|
||||
public static CategoryApi getCategoryApi(Context context) {
|
||||
return getClient(context).create(CategoryApi.class);
|
||||
}
|
||||
|
||||
public static RefundApi getRefundApi(Context context) {
|
||||
return getClient(context).create(RefundApi.class);
|
||||
}
|
||||
|
||||
public static EmployeeApi getEmployeeApi(Context context) {
|
||||
return getClient(context).create(EmployeeApi.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,6 +8,8 @@ import retrofit2.http.GET;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
public interface UserApi {
|
||||
String AVATAR_PATH = "api/v1/users/%d/avatar/file";
|
||||
|
||||
@GET("api/v1/users")
|
||||
Call<PageResponse<UserDTO>> getUsers(@Query("role") String role, @Query("page") int page, @Query("size") int size);
|
||||
}
|
||||
|
||||
@@ -191,4 +191,10 @@ public class NetworkModule {
|
||||
public static RefundApi provideRefundApi(Retrofit retrofit) {
|
||||
return retrofit.create(RefundApi.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public static CouponApi provideCouponApi(Retrofit retrofit) {
|
||||
return retrofit.create(CouponApi.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.example.petstoremobile.dtos;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class CouponDTO {
|
||||
private Long couponId;
|
||||
private String couponCode;
|
||||
private String discountType; // FIXED, PERCENT
|
||||
private BigDecimal discountValue;
|
||||
private BigDecimal minOrderAmount;
|
||||
private Boolean active;
|
||||
private String startsAt;
|
||||
private String endsAt;
|
||||
private Integer usageLimit;
|
||||
private String createdAt;
|
||||
private String updatedAt;
|
||||
|
||||
public CouponDTO() {}
|
||||
|
||||
public Long getCouponId() { return couponId; }
|
||||
public void setCouponId(Long couponId) { this.couponId = couponId; }
|
||||
|
||||
public String getCouponCode() { return couponCode; }
|
||||
public void setCouponCode(String couponCode) { this.couponCode = couponCode; }
|
||||
|
||||
public String getDiscountType() { return discountType; }
|
||||
public void setDiscountType(String discountType) { this.discountType = discountType; }
|
||||
|
||||
public BigDecimal getDiscountValue() { return discountValue; }
|
||||
public void setDiscountValue(BigDecimal discountValue) { this.discountValue = discountValue; }
|
||||
|
||||
public BigDecimal getMinOrderAmount() { return minOrderAmount; }
|
||||
public void setMinOrderAmount(BigDecimal minOrderAmount) { this.minOrderAmount = minOrderAmount; }
|
||||
|
||||
public Boolean getActive() { return active; }
|
||||
public void setActive(Boolean active) { this.active = active; }
|
||||
|
||||
public String getStartsAt() { return startsAt; }
|
||||
public void setStartsAt(String startsAt) { this.startsAt = startsAt; }
|
||||
|
||||
public String getEndsAt() { return endsAt; }
|
||||
public void setEndsAt(String endsAt) { this.endsAt = endsAt; }
|
||||
|
||||
public Integer getUsageLimit() { return usageLimit; }
|
||||
public void setUsageLimit(Integer usageLimit) { this.usageLimit = usageLimit; }
|
||||
|
||||
public String getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
public String getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(String updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -92,6 +92,7 @@ public class ListFragment extends Fragment {
|
||||
binding.drawerSale.setOnClickListener(v -> navigateTo(R.id.nav_sale));
|
||||
binding.drawerStaff.setOnClickListener(v -> navigateTo(R.id.nav_staff));
|
||||
binding.drawerAnalytics.setOnClickListener(v -> navigateTo(R.id.nav_analytics));
|
||||
binding.drawerCoupons.setOnClickListener(v -> navigateTo(R.id.nav_coupon));
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.example.petstoremobile.fragments.listfragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.Navigation;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.CouponAdapter;
|
||||
import com.example.petstoremobile.databinding.FragmentCouponBinding;
|
||||
import com.example.petstoremobile.dtos.CouponDTO;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.CouponListViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class CouponFragment extends Fragment implements CouponAdapter.OnCouponClickListener {
|
||||
private FragmentCouponBinding binding;
|
||||
private CouponListViewModel viewModel;
|
||||
private CouponAdapter adapter;
|
||||
private final List<CouponDTO> couponList = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentCouponBinding.inflate(inflater, container, false);
|
||||
viewModel = new ViewModelProvider(this).get(CouponListViewModel.class);
|
||||
|
||||
setupRecyclerView();
|
||||
setupSearch();
|
||||
setupStatusFilter();
|
||||
setupTypeFilter();
|
||||
setupSwipeRefresh();
|
||||
observeViewModel();
|
||||
|
||||
viewModel.loadCoupons(0, 100, null, null, null);
|
||||
|
||||
binding.fabAddCoupon.setOnClickListener(v -> openDetail(-1));
|
||||
binding.btnBulkDeleteCoupons.setOnClickListener(v -> confirmBulkDelete());
|
||||
|
||||
UIUtils.setupHamburgerMenu(binding.btnHamburgerCoupon, this);
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilterCoupon, binding.layoutFilterCoupon, binding.etSearchCoupon,
|
||||
binding.spinnerTypeCoupon, binding.spinnerStatusCoupon);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void observeViewModel() {
|
||||
viewModel.getCoupons().observe(getViewLifecycleOwner(), list -> {
|
||||
couponList.clear();
|
||||
couponList.addAll(list);
|
||||
adapter.notifyDataSetChanged();
|
||||
});
|
||||
|
||||
viewModel.getIsLoading().observe(getViewLifecycleOwner(), loading -> {
|
||||
binding.swipeRefreshCoupon.setRefreshing(loading);
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
adapter = new CouponAdapter(couponList, this);
|
||||
binding.recyclerViewCoupon.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerViewCoupon.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Active", "Inactive"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusCoupon, statuses, this::applyFilters);
|
||||
}
|
||||
|
||||
private void setupTypeFilter() {
|
||||
String[] types = {"All Types", "FIXED", "PERCENT"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerTypeCoupon, types, this::applyFilters);
|
||||
}
|
||||
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchCoupon, this::applyFilters);
|
||||
}
|
||||
|
||||
private void setupSwipeRefresh() {
|
||||
binding.swipeRefreshCoupon.setOnRefreshListener(this::applyFilters);
|
||||
}
|
||||
|
||||
private void applyFilters() {
|
||||
String statusStr = binding.spinnerStatusCoupon.getSelectedItem() != null ?
|
||||
binding.spinnerStatusCoupon.getSelectedItem().toString() : "All Statuses";
|
||||
Boolean active = null;
|
||||
if (statusStr.equals("Active")) active = true;
|
||||
else if (statusStr.equals("Inactive")) active = false;
|
||||
|
||||
String typeStr = binding.spinnerTypeCoupon.getSelectedItem() != null ?
|
||||
binding.spinnerTypeCoupon.getSelectedItem().toString() : "All Types";
|
||||
String discountType = typeStr.equals("All Types") ? null : typeStr;
|
||||
|
||||
viewModel.loadCoupons(0, 100, active, discountType, null);
|
||||
}
|
||||
|
||||
private void openDetail(long id) {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("couponId", id);
|
||||
Navigation.findNavController(requireView()).navigate(R.id.couponDetailFragment, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCouponClick(CouponDTO coupon) {
|
||||
openDetail(coupon.getCouponId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectionChanged(int count) {
|
||||
binding.btnBulkDeleteCoupons.setVisibility(count > 0 ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void confirmBulkDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Confirm Bulk Delete")
|
||||
.setMessage("Are you sure you want to delete the selected coupons?")
|
||||
.setPositiveButton("Delete", (dialog, which) -> {
|
||||
List<Long> ids = new ArrayList<>(adapter.getSelectedIds());
|
||||
viewModel.bulkDeleteCoupons(ids).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
adapter.setSelectionMode(false);
|
||||
applyFilters();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
UIUtils.showToast(requireContext(), resource.message);
|
||||
}
|
||||
});
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,19 @@ import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.adapters.EmployeeAdapter;
|
||||
import com.example.petstoremobile.api.auth.TokenManager;
|
||||
import com.example.petstoremobile.databinding.FragmentStaffBinding;
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.utils.SpinnerUtils;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.StaffListViewModel;
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
import java.util.*;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmployeeClickListener {
|
||||
|
||||
@@ -25,6 +31,9 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
private List<EmployeeDTO> staffList = new ArrayList<>();
|
||||
private EmployeeAdapter adapter;
|
||||
|
||||
@Inject @Named("baseUrl") String baseUrl;
|
||||
@Inject TokenManager tokenManager;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
@@ -33,15 +42,19 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
|
||||
setupRecyclerView();
|
||||
setupSearch();
|
||||
setupStatusFilter();
|
||||
setupStoreFilter();
|
||||
setupSwipeRefresh();
|
||||
observeViewModel();
|
||||
|
||||
viewModel.loadStaff();
|
||||
viewModel.loadStores();
|
||||
|
||||
binding.fabAddStaff.setOnClickListener(v -> openDetail(-1));
|
||||
|
||||
UIUtils.setupHamburgerMenu(binding.btnHamburgerStaff, this);
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilterStaff, binding.layoutFilterStaff, binding.etSearchStaff);
|
||||
UIUtils.setupFilterToggle(binding.btnToggleFilterStaff, binding.layoutFilterStaff, binding.etSearchStaff,
|
||||
binding.spinnerStoreStaff, binding.spinnerStatusStaff);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
@@ -53,6 +66,11 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
adapter.notifyDataSetChanged();
|
||||
});
|
||||
|
||||
viewModel.getStores().observe(getViewLifecycleOwner(), list -> {
|
||||
SpinnerUtils.populateWhiteSpinner(requireContext(), binding.spinnerStoreStaff, list,
|
||||
StoreDTO::getStoreName, "All Stores", -1L, StoreDTO::getStoreId);
|
||||
});
|
||||
|
||||
viewModel.getIsLoading().observe(getViewLifecycleOwner(), loading -> {
|
||||
binding.swipeRefreshStaff.setRefreshing(loading);
|
||||
});
|
||||
@@ -60,12 +78,37 @@ public class StaffFragment extends Fragment implements EmployeeAdapter.OnEmploye
|
||||
|
||||
private void setupRecyclerView() {
|
||||
adapter = new EmployeeAdapter(staffList, this);
|
||||
adapter.setBaseUrl(baseUrl);
|
||||
adapter.setToken(tokenManager.getToken());
|
||||
binding.recyclerViewStaff.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerViewStaff.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void setupStatusFilter() {
|
||||
String[] statuses = {"All Statuses", "Active", "Inactive"};
|
||||
SpinnerUtils.setupStringFilterSpinner(requireContext(), binding.spinnerStatusStaff, statuses, this::applyFilters);
|
||||
}
|
||||
|
||||
private void setupStoreFilter() {
|
||||
SpinnerUtils.setupFilterSpinner(binding.spinnerStoreStaff, this::applyFilters);
|
||||
}
|
||||
|
||||
private void setupSearch() {
|
||||
UIUtils.attachSearch(binding.etSearchStaff, () -> viewModel.filter(binding.etSearchStaff.getText().toString()));
|
||||
UIUtils.attachSearch(binding.etSearchStaff, this::applyFilters);
|
||||
}
|
||||
|
||||
private void applyFilters() {
|
||||
String query = binding.etSearchStaff.getText().toString().trim();
|
||||
String status = binding.spinnerStatusStaff.getSelectedItem() != null ?
|
||||
binding.spinnerStatusStaff.getSelectedItem().toString() : "All Statuses";
|
||||
|
||||
Long storeId = null;
|
||||
List<StoreDTO> stores = viewModel.getStores().getValue();
|
||||
if (binding.spinnerStoreStaff.getSelectedItemPosition() > 0 && stores != null && !stores.isEmpty()) {
|
||||
storeId = stores.get(binding.spinnerStoreStaff.getSelectedItemPosition() - 1).getStoreId();
|
||||
}
|
||||
|
||||
viewModel.filter(query, storeId, status);
|
||||
}
|
||||
|
||||
private void setupSwipeRefresh() {
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
import com.example.petstoremobile.R;
|
||||
import com.example.petstoremobile.databinding.FragmentCouponDetailBinding;
|
||||
import com.example.petstoremobile.dtos.CouponDTO;
|
||||
import com.example.petstoremobile.utils.InputValidator;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
import com.example.petstoremobile.utils.UIUtils;
|
||||
import com.example.petstoremobile.viewmodels.CouponDetailViewModel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class CouponDetailFragment extends Fragment {
|
||||
private FragmentCouponDetailBinding binding;
|
||||
private CouponDetailViewModel viewModel;
|
||||
private long couponId = -1;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentCouponDetailBinding.inflate(inflater, container, false);
|
||||
viewModel = new ViewModelProvider(this).get(CouponDetailViewModel.class);
|
||||
|
||||
if (getArguments() != null) {
|
||||
couponId = getArguments().getLong("couponId", -1);
|
||||
viewModel.setCouponId(couponId, couponId > 0);
|
||||
}
|
||||
|
||||
setupDiscountTypeSpinner();
|
||||
setupDatePicker(binding.etStartsAtDetail, null, () -> {
|
||||
String selectedDate = binding.etStartsAtDetail.getText().toString();
|
||||
if (selectedDate.isEmpty()) {
|
||||
binding.etEndsAtDetail.setText("");
|
||||
} else {
|
||||
String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date());
|
||||
if (selectedDate.equals(today)) {
|
||||
binding.cbActiveDetail.setChecked(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
setupDatePicker(binding.etEndsAtDetail, binding.etStartsAtDetail, null);
|
||||
|
||||
if (couponId > 0) {
|
||||
loadCouponDetails();
|
||||
binding.btnDeleteCouponDetail.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.tvTitleCouponDetail.setText("Add Coupon");
|
||||
}
|
||||
|
||||
binding.btnBackCouponDetail.setOnClickListener(v -> Navigation.findNavController(v).popBackStack());
|
||||
binding.btnSaveCouponDetail.setOnClickListener(v -> saveCoupon());
|
||||
binding.btnDeleteCouponDetail.setOnClickListener(v -> confirmDelete());
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void setupDiscountTypeSpinner() {
|
||||
String[] types = {"FIXED", "PERCENT"};
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(requireContext(), android.R.layout.simple_spinner_item, types);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
binding.spinnerDiscountTypeDetail.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void setupDatePicker(android.widget.EditText editText, android.widget.EditText dependOn, Runnable onDateSet) {
|
||||
editText.setFocusable(false);
|
||||
editText.setClickable(true);
|
||||
editText.setCursorVisible(false);
|
||||
editText.setOnClickListener(v -> {
|
||||
if (dependOn != null && dependOn.getText().toString().trim().isEmpty()) {
|
||||
UIUtils.showToast(requireContext(), "Please select a start date first");
|
||||
return;
|
||||
}
|
||||
UIUtils.showDatePicker(requireContext(), editText, onDateSet);
|
||||
});
|
||||
}
|
||||
|
||||
private void loadCouponDetails() {
|
||||
viewModel.loadCoupon(couponId).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
CouponDTO coupon = resource.data;
|
||||
binding.etCouponCodeDetail.setText(coupon.getCouponCode());
|
||||
binding.spinnerDiscountTypeDetail.setSelection(coupon.getDiscountType().equals("FIXED") ? 0 : 1);
|
||||
binding.etDiscountValueDetail.setText(coupon.getDiscountValue().stripTrailingZeros().toPlainString());
|
||||
binding.etMinOrderAmountDetail.setText(coupon.getMinOrderAmount().stripTrailingZeros().toPlainString());
|
||||
binding.etUsageLimitDetail.setText(coupon.getUsageLimit() != null ? String.valueOf(coupon.getUsageLimit()) : "");
|
||||
binding.cbActiveDetail.setChecked(Boolean.TRUE.equals(coupon.getActive()));
|
||||
|
||||
if (coupon.getStartsAt() != null) binding.etStartsAtDetail.setText(coupon.getStartsAt().substring(0, 10));
|
||||
if (coupon.getEndsAt() != null) binding.etEndsAtDetail.setText(coupon.getEndsAt().substring(0, 10));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void saveCoupon() {
|
||||
if (!InputValidator.isNotEmpty(binding.etCouponCodeDetail, "Coupon Code")) return;
|
||||
if (!InputValidator.isGreaterThanZero(binding.etDiscountValueDetail, "Discount Value")) return;
|
||||
if (!InputValidator.isPositiveDecimal(binding.etMinOrderAmountDetail, "Min Order Amount")) return;
|
||||
|
||||
String startsAt = binding.etStartsAtDetail.getText().toString().trim();
|
||||
String endsAt = binding.etEndsAtDetail.getText().toString().trim();
|
||||
|
||||
if (!startsAt.isEmpty() && !endsAt.isEmpty()) {
|
||||
if (startsAt.compareTo(endsAt) > 0) {
|
||||
binding.etEndsAtDetail.setError("End date must be after start date");
|
||||
UIUtils.showToast(requireContext(), "End date must be after start date");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
String usageLimitStr = binding.etUsageLimitDetail.getText().toString().trim();
|
||||
if (!usageLimitStr.isEmpty()) {
|
||||
if (!InputValidator.isPositiveInteger(binding.etUsageLimitDetail, "Usage Limit")) return;
|
||||
}
|
||||
|
||||
CouponDTO dto = new CouponDTO();
|
||||
dto.setCouponCode(binding.etCouponCodeDetail.getText().toString().trim().toUpperCase());
|
||||
dto.setDiscountType(binding.spinnerDiscountTypeDetail.getSelectedItem().toString());
|
||||
dto.setDiscountValue(new BigDecimal(binding.etDiscountValueDetail.getText().toString().trim()));
|
||||
dto.setMinOrderAmount(new BigDecimal(binding.etMinOrderAmountDetail.getText().toString().trim()));
|
||||
|
||||
String usageLimit = binding.etUsageLimitDetail.getText().toString().trim();
|
||||
dto.setUsageLimit(usageLimit.isEmpty() ? null : Integer.parseInt(usageLimit));
|
||||
|
||||
dto.setActive(binding.cbActiveDetail.isChecked());
|
||||
|
||||
dto.setStartsAt(startsAt.isEmpty() ? null : startsAt + "T00:00:00");
|
||||
|
||||
dto.setEndsAt(endsAt.isEmpty() ? null : endsAt + "T23:59:59");
|
||||
|
||||
viewModel.saveCoupon(dto).observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Navigation.findNavController(requireView()).popBackStack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
UIUtils.showToast(requireContext(), resource.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void confirmDelete() {
|
||||
new AlertDialog.Builder(requireContext())
|
||||
.setTitle("Delete Coupon")
|
||||
.setMessage("Are you sure you want to delete this coupon?")
|
||||
.setPositiveButton("Delete", (dialog, which) -> {
|
||||
viewModel.deleteCoupon().observe(getViewLifecycleOwner(), resource -> {
|
||||
if (resource.status == Resource.Status.SUCCESS) {
|
||||
Navigation.findNavController(requireView()).popBackStack();
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
UIUtils.showToast(requireContext(), resource.message);
|
||||
}
|
||||
});
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.example.petstoremobile.repositories;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.example.petstoremobile.api.CouponApi;
|
||||
import com.example.petstoremobile.dtos.CouponDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class CouponRepository extends BaseRepository {
|
||||
private final CouponApi couponApi;
|
||||
|
||||
@Inject
|
||||
public CouponRepository(CouponApi couponApi) {
|
||||
super("CouponRepository");
|
||||
this.couponApi = couponApi;
|
||||
}
|
||||
|
||||
public LiveData<Resource<PageResponse<CouponDTO>>> getAllCoupons(int page, int size, Boolean active, String discountType, String sort) {
|
||||
return executeCall(couponApi.getAllCoupons(page, size, active, discountType, sort));
|
||||
}
|
||||
|
||||
public LiveData<Resource<CouponDTO>> getCouponById(Long id) {
|
||||
return executeCall(couponApi.getCouponById(id));
|
||||
}
|
||||
|
||||
public LiveData<Resource<CouponDTO>> createCoupon(CouponDTO coupon) {
|
||||
return executeCall(couponApi.createCoupon(coupon));
|
||||
}
|
||||
|
||||
public LiveData<Resource<CouponDTO>> updateCoupon(Long id, CouponDTO coupon) {
|
||||
return executeCall(couponApi.updateCoupon(id, coupon));
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteCoupon(Long id) {
|
||||
return executeCall(couponApi.deleteCoupon(id));
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> bulkDeleteCoupons(List<Long> ids) {
|
||||
return executeCall(couponApi.bulkDeleteCoupons(ids));
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ public class ErrorUtils {
|
||||
try {
|
||||
if (response.errorBody() != null) {
|
||||
String errorJson = response.errorBody().string();
|
||||
Log.e(TAG, "Full Error JSON: " + errorJson);
|
||||
ErrorResponse errorResponse = gson.fromJson(errorJson, ErrorResponse.class);
|
||||
if (errorResponse != null && errorResponse.getMessage() != null) {
|
||||
return errorResponse.getMessage();
|
||||
|
||||
@@ -35,6 +35,24 @@ public class InputValidator {
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the value is a positive decimal number greater than 0
|
||||
public static boolean isGreaterThanZero(EditText field, String fieldName) {
|
||||
String value = field.getText().toString().trim();
|
||||
try {
|
||||
double num = Double.parseDouble(value);
|
||||
if (num <= 0) {
|
||||
field.setError(fieldName + " must be greater than 0");
|
||||
field.requestFocus();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
field.setError(fieldName + " must be a number");
|
||||
field.requestFocus();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Checks if the value is a positive decimal number
|
||||
public static boolean isPositiveDecimal(EditText field, String fieldName) {
|
||||
String value = field.getText().toString().trim();
|
||||
|
||||
@@ -134,6 +134,10 @@ public class UIUtils {
|
||||
},
|
||||
c.get(Calendar.YEAR), c.get(Calendar.MONTH),
|
||||
c.get(Calendar.DAY_OF_MONTH));
|
||||
d.setButton(DatePickerDialog.BUTTON_NEUTRAL, "Clear", (dialog, which) -> {
|
||||
editText.setText("");
|
||||
if (onDateSet != null) onDateSet.run();
|
||||
});
|
||||
d.getDatePicker().setMinDate(System.currentTimeMillis() - 1000);
|
||||
d.show();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.CouponDTO;
|
||||
import com.example.petstoremobile.repositories.CouponRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class CouponDetailViewModel extends ViewModel {
|
||||
private final CouponRepository repository;
|
||||
private long couponId = -1;
|
||||
private boolean isEditing = false;
|
||||
|
||||
@Inject
|
||||
public CouponDetailViewModel(CouponRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public LiveData<Resource<CouponDTO>> loadCoupon(long id) {
|
||||
return repository.getCouponById(id);
|
||||
}
|
||||
|
||||
public void setCouponId(long id, boolean isEditing) {
|
||||
this.couponId = id;
|
||||
this.isEditing = isEditing;
|
||||
}
|
||||
|
||||
public long getCouponId() {
|
||||
return couponId;
|
||||
}
|
||||
|
||||
public boolean isEditing() {
|
||||
return isEditing;
|
||||
}
|
||||
|
||||
public LiveData<Resource<CouponDTO>> saveCoupon(CouponDTO dto) {
|
||||
if (isEditing && couponId > 0) {
|
||||
return repository.updateCoupon(couponId, dto);
|
||||
} else {
|
||||
return repository.createCoupon(dto);
|
||||
}
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> deleteCoupon() {
|
||||
return repository.deleteCoupon(couponId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.example.petstoremobile.viewmodels;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.CouponDTO;
|
||||
import com.example.petstoremobile.dtos.PageResponse;
|
||||
import com.example.petstoremobile.repositories.CouponRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
|
||||
@HiltViewModel
|
||||
public class CouponListViewModel extends ViewModel {
|
||||
private final CouponRepository repository;
|
||||
private final MutableLiveData<List<CouponDTO>> coupons = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
|
||||
|
||||
@Inject
|
||||
public CouponListViewModel(CouponRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public LiveData<List<CouponDTO>> getCoupons() { return coupons; }
|
||||
public LiveData<Boolean> getIsLoading() { return isLoading; }
|
||||
|
||||
public void loadCoupons(int page, int size, Boolean active, String discountType, String sort) {
|
||||
isLoading.setValue(true);
|
||||
repository.getAllCoupons(page, size, active, discountType, sort).observeForever(resource -> {
|
||||
if (resource != null) {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
coupons.setValue(resource.data.getContent());
|
||||
isLoading.setValue(false);
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
isLoading.setValue(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public LiveData<Resource<Void>> bulkDeleteCoupons(List<Long> ids) {
|
||||
return repository.bulkDeleteCoupons(ids);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,9 @@ import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.example.petstoremobile.dtos.EmployeeDTO;
|
||||
import com.example.petstoremobile.dtos.StoreDTO;
|
||||
import com.example.petstoremobile.repositories.EmployeeRepository;
|
||||
import com.example.petstoremobile.repositories.StoreRepository;
|
||||
import com.example.petstoremobile.utils.Resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -18,18 +20,24 @@ import dagger.hilt.android.lifecycle.HiltViewModel;
|
||||
@HiltViewModel
|
||||
public class StaffListViewModel extends ViewModel {
|
||||
private final EmployeeRepository repository;
|
||||
private final StoreRepository storeRepository;
|
||||
|
||||
private final MutableLiveData<List<EmployeeDTO>> employees = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<EmployeeDTO>> filteredEmployees = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<List<StoreDTO>> stores = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);
|
||||
private String lastQuery = "";
|
||||
private Long lastStoreId = null;
|
||||
private String lastStatus = "All Statuses";
|
||||
|
||||
@Inject
|
||||
public StaffListViewModel(EmployeeRepository repository) {
|
||||
public StaffListViewModel(EmployeeRepository repository, StoreRepository storeRepository) {
|
||||
this.repository = repository;
|
||||
this.storeRepository = storeRepository;
|
||||
}
|
||||
|
||||
public LiveData<List<EmployeeDTO>> getFilteredEmployees() { return filteredEmployees; }
|
||||
public LiveData<List<StoreDTO>> getStores() { return stores; }
|
||||
public LiveData<Boolean> getIsLoading() { return isLoading; }
|
||||
|
||||
public void loadStaff() {
|
||||
@@ -38,7 +46,7 @@ public class StaffListViewModel extends ViewModel {
|
||||
if (resource != null) {
|
||||
if (resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
employees.setValue(resource.data.getContent());
|
||||
filter(lastQuery);
|
||||
filter(lastQuery, lastStoreId, lastStatus);
|
||||
isLoading.setValue(false);
|
||||
} else if (resource.status == Resource.Status.ERROR) {
|
||||
isLoading.setValue(false);
|
||||
@@ -47,25 +55,45 @@ public class StaffListViewModel extends ViewModel {
|
||||
});
|
||||
}
|
||||
|
||||
public void filter(String query) {
|
||||
public void loadStores() {
|
||||
storeRepository.getAllStores(0, 100).observeForever(resource -> {
|
||||
if (resource != null && resource.status == Resource.Status.SUCCESS && resource.data != null) {
|
||||
stores.setValue(resource.data.getContent());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void filter(String query, Long storeId, String status) {
|
||||
this.lastQuery = query;
|
||||
this.lastStoreId = storeId;
|
||||
this.lastStatus = status;
|
||||
|
||||
List<EmployeeDTO> all = employees.getValue();
|
||||
if (all == null) return;
|
||||
|
||||
if (query.isEmpty()) {
|
||||
filteredEmployees.setValue(new ArrayList<>(all));
|
||||
} else {
|
||||
List<EmployeeDTO> filtered = new ArrayList<>();
|
||||
String lower = query.toLowerCase();
|
||||
for (EmployeeDTO e : all) {
|
||||
if ((e.getFullName() != null && e.getFullName().toLowerCase().contains(lower))
|
||||
|| (e.getUsername() != null && e.getUsername().toLowerCase().contains(lower))
|
||||
|| (e.getEmail() != null && e.getEmail().toLowerCase().contains(lower))
|
||||
|| (e.getPhone() != null && e.getPhone().toLowerCase().contains(lower))) {
|
||||
filtered.add(e);
|
||||
}
|
||||
List<EmployeeDTO> filtered = new ArrayList<>();
|
||||
String lowerQuery = query.toLowerCase();
|
||||
|
||||
for (EmployeeDTO e : all) {
|
||||
// Search Query Filter
|
||||
boolean matchesQuery = query.isEmpty() ||
|
||||
(e.getFullName() != null && e.getFullName().toLowerCase().contains(lowerQuery)) ||
|
||||
(e.getUsername() != null && e.getUsername().toLowerCase().contains(lowerQuery)) ||
|
||||
(e.getEmail() != null && e.getEmail().toLowerCase().contains(lowerQuery)) ||
|
||||
(e.getPhone() != null && e.getPhone().toLowerCase().contains(lowerQuery));
|
||||
|
||||
// Store Filter
|
||||
boolean matchesStore = storeId == null || (e.getPrimaryStoreId() != null && e.getPrimaryStoreId().equals(storeId));
|
||||
|
||||
// Status Filter
|
||||
boolean matchesStatus = status.equals("All Statuses") ||
|
||||
(status.equals("Active") && Boolean.TRUE.equals(e.getActive())) ||
|
||||
(status.equals("Inactive") && Boolean.FALSE.equals(e.getActive()));
|
||||
|
||||
if (matchesQuery && matchesStore && matchesStatus) {
|
||||
filtered.add(e);
|
||||
}
|
||||
filteredEmployees.setValue(filtered);
|
||||
}
|
||||
filteredEmployees.setValue(filtered);
|
||||
}
|
||||
}
|
||||
|
||||
160
android/app/src/main/res/layout/fragment_coupon.xml
Normal file
160
android/app/src/main/res/layout/fragment_coupon.xml
Normal file
@@ -0,0 +1,160 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_grey">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/headerCoupon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/primary_dark"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnHamburgerCoupon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@drawable/baseline_menu_36"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="Open menu"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Coupons"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginStart="8dp"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnBulkDeleteCoupons"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@android:drawable/ic_menu_delete"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
app:tint="@color/white"
|
||||
android:contentDescription="Bulk Delete Coupons"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/btnToggleFilterCoupon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:src="@android:drawable/ic_menu_search"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
app:tint="@color/white"
|
||||
android:contentDescription="Toggle filter"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutFilterCoupon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:visibility="gone"
|
||||
android:background="@color/primary_dark"
|
||||
android:elevation="4dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="44dp"
|
||||
android:background="@drawable/bg_search_bar"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="18dp"
|
||||
android:src="@android:drawable/ic_menu_search"
|
||||
android:alpha="0.6"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etSearchCoupon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:hint="Search coupons..."
|
||||
android:inputType="text"
|
||||
android:background="@android:color/transparent"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textColorHint="#99000000"
|
||||
android:textSize="14sp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerTypeCoupon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_spinner"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerStatusCoupon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_spinner"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:layout_marginStart="4dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshCoupon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerViewCoupon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp"/>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fabAddCoupon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:contentDescription="Add Coupon"
|
||||
app:srcCompat="@android:drawable/ic_input_add"
|
||||
app:tint="@color/white"/>
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
258
android/app/src/main/res/layout/fragment_coupon_detail.xml
Normal file
258
android/app/src/main/res/layout/fragment_coupon_detail.xml
Normal file
@@ -0,0 +1,258 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/background_grey">
|
||||
|
||||
<!-- Top Bar -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:background="@color/primary_dark"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvTitleCouponDetail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Coupon Detail"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnDeleteCouponDetail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:text="Delete"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/rounded_card"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<!-- Coupon Code -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Coupon Code"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etCouponCodeDetail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="Enter coupon code"
|
||||
android:inputType="textCapCharacters"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Discount Type -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Discount Type"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerDiscountTypeDetail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Discount Value -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Discount Value"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etDiscountValueDetail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="0.00"
|
||||
android:inputType="numberDecimal"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Min Order Amount -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Min Order Amount"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etMinOrderAmountDetail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="0.00"
|
||||
android:inputType="numberDecimal"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Usage Limit -->
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Usage Limit"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etUsageLimitDetail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="No limit"
|
||||
android:inputType="number"
|
||||
android:layout_marginBottom="16dp"/>
|
||||
|
||||
<!-- Dates -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginEnd="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Starts At"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etStartsAtDetail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="YYYY-MM-DD"
|
||||
android:focusable="false"
|
||||
android:clickable="true"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="8dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Ends At"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etEndsAtDetail"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="YYYY-MM-DD"
|
||||
android:focusable="false"
|
||||
android:clickable="true"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Active -->
|
||||
<CheckBox
|
||||
android:id="@+id/cbActiveDetail"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Active"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="14sp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<!-- Bottom Action Bar -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="@color/white"
|
||||
android:padding="16dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnBackCouponDetail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="Back"
|
||||
android:backgroundTint="@color/primary_medium"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnSaveCouponDetail"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="8dp"
|
||||
android:text="Save"
|
||||
android:backgroundTint="@color/accent_coral"
|
||||
android:textColor="@color/white"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/accent_coral"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -34,9 +34,10 @@
|
||||
android:layout_width="260dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:orientation="vertical"
|
||||
android:background="@color/primary_dark">
|
||||
android:background="@color/primary_dark"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Header: Logo and Store Name (Static) -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -69,221 +70,255 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="MANAGE"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="11sp"
|
||||
android:letterSpacing="0.15"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="24dp"
|
||||
android:paddingBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerPets"
|
||||
<!-- Scrollable Menu Items -->
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pets"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="24dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerServices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Services"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="MANAGE"
|
||||
android:textColor="@color/text_light"
|
||||
android:textSize="11sp"
|
||||
android:letterSpacing="0.15"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingTop="24dp"
|
||||
android:paddingBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerSuppliers"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Suppliers"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerPets"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Pets"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerAppointments"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Appointments"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerServices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Services"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerAdoptions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Adoptions"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerSuppliers"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Suppliers"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerInventory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Inventory"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerAppointments"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Appointments"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerProducts"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Products"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerAdoptions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Adoptions"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerSale"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sale"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerInventory"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Inventory"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerAnalytics"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Analytics"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerProducts"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Products"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerStaff"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:visibility="gone">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Staff Accounts"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerSale"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sale"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerPurchaseOrderView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="PurchaseOrder"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerAnalytics"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Analytics"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerProductSupplier"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="ProductSupplier"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerStaff"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:visibility="gone">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Staff Accounts"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerPurchaseOrderView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="PurchaseOrder"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerProductSupplier"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="ProductSupplier"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/drawerCoupons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?attr/selectableItemBackground">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Coupons"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -91,6 +91,34 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerStoreStaff"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_spinner"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:layout_marginEnd="4dp"/>
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinnerStatusStaff"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_spinner"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:layout_marginStart="4dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
|
||||
92
android/app/src/main/res/layout/item_coupon.xml
Normal file
92
android/app/src/main/res/layout/item_coupon.xml
Normal file
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:background="@color/white"
|
||||
android:layout_marginBottom="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/cbSelectCoupon"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCouponCode"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="COUPONCODE"
|
||||
android:textColor="@color/text_dark"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCouponStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:text="ACTIVE"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="11sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCouponDiscount"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="10% OFF"
|
||||
android:textColor="@color/accent_coral"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCouponMinOrder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="Min order: $50.00"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCouponExpiry"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:text="Expires: 2026-12-31"
|
||||
android:textColor="#888888"
|
||||
android:textSize="13sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/ivEmployeeProfile"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/placeholder"
|
||||
|
||||
@@ -157,4 +157,21 @@
|
||||
android:label="Analytics"
|
||||
tools:layout="@layout/fragment_analytics" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_coupon"
|
||||
android:name="com.example.petstoremobile.fragments.listfragments.CouponFragment"
|
||||
android:label="Coupons"
|
||||
tools:layout="@layout/fragment_coupon" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/couponDetailFragment"
|
||||
android:name="com.example.petstoremobile.fragments.listfragments.detailfragments.CouponDetailFragment"
|
||||
android:label="Coupon Details"
|
||||
tools:layout="@layout/fragment_coupon_detail">
|
||||
<argument
|
||||
android:name="couponId"
|
||||
app:argType="long"
|
||||
android:defaultValue="-1L" />
|
||||
</fragment>
|
||||
|
||||
</navigation>
|
||||
|
||||
Reference in New Issue
Block a user