Add 'android/' from commit '4e3261e9879f36679271f2dbdf6023ad01334265'
git-subtree-dir: android
git-subtree-mainline: 58b37b52384ca0a702c23297a2cd9bdad595c660
git-subtree-split: 4e3261e987
This commit is contained in:
10
android/.gitignore
vendored
Normal file
10
android/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
1
android/app/.gitignore
vendored
Normal file
1
android/app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
62
android/app/build.gradle.kts
Normal file
62
android/app/build.gradle.kts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.android.application)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "com.example.petstoremobile"
|
||||||
|
compileSdk = 36
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
applicationId = "com.example.petstoremobile"
|
||||||
|
minSdk = 24
|
||||||
|
targetSdk = 36
|
||||||
|
versionCode = 1
|
||||||
|
versionName = "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = false
|
||||||
|
proguardFiles(
|
||||||
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
|
"proguard-rules.pro"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.appcompat)
|
||||||
|
implementation(libs.material)
|
||||||
|
implementation(libs.activity)
|
||||||
|
implementation(libs.constraintlayout)
|
||||||
|
|
||||||
|
implementation("com.squareup.retrofit2:retrofit:2.9.0")
|
||||||
|
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
|
||||||
|
implementation("com.squareup.okhttp3:logging-interceptor:4.9.1")
|
||||||
|
implementation("com.squareup.okhttp3:okhttp:4.12.0")
|
||||||
|
|
||||||
|
implementation("com.google.android.material:material:1.11.0")
|
||||||
|
implementation("androidx.viewpager2:viewpager2:1.1.0")
|
||||||
|
|
||||||
|
implementation("androidx.camera:camera-core:1.4.0")
|
||||||
|
implementation("androidx.camera:camera-camera2:1.4.0")
|
||||||
|
implementation("androidx.camera:camera-lifecycle:1.4.0")
|
||||||
|
implementation("androidx.camera:camera-view:1.4.0")
|
||||||
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
|
implementation(libs.swiperefreshlayout)
|
||||||
|
|
||||||
|
implementation("com.github.NaikSoftware:StompProtocolAndroid:1.6.6")
|
||||||
|
implementation("io.reactivex.rxjava2:rxjava:2.2.21")
|
||||||
|
implementation("io.reactivex.rxjava2:rxandroid:2.1.1")
|
||||||
|
|
||||||
|
testImplementation(libs.junit)
|
||||||
|
androidTestImplementation(libs.ext.junit)
|
||||||
|
androidTestImplementation(libs.espresso.core)
|
||||||
|
}
|
||||||
21
android/app/proguard-rules.pro
vendored
Normal file
21
android/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
52
android/app/src/main/AndroidManifest.xml
Normal file
52
android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission
|
||||||
|
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||||
|
android:maxSdkVersion="32" />
|
||||||
|
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.camera"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:name=".PetStoreApplication"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/Theme.PetStoreMobile">
|
||||||
|
<activity
|
||||||
|
android:name=".activities.HomeActivity"
|
||||||
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
android:exported="false" />
|
||||||
|
<activity
|
||||||
|
android:name=".activities.MainActivity"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name="androidx.core.content.FileProvider"
|
||||||
|
android:authorities="${applicationId}.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/file_paths" />
|
||||||
|
</provider>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.example.petstoremobile;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import com.example.petstoremobile.api.auth.TokenManager;
|
||||||
|
|
||||||
|
public class PetStoreApplication extends Application {
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
// Clear login data on app so when the application closes, the user is logged out and have to re-login
|
||||||
|
TokenManager.getInstance(this).clearLoginData();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.example.petstoremobile.activities;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
import androidx.core.view.WindowInsetsControllerCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.fragments.ChatFragment;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.ProfileFragment;
|
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
|
|
||||||
|
public class HomeActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_home);
|
||||||
|
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
//get the bottom navbar from the layout
|
||||||
|
BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation);
|
||||||
|
|
||||||
|
// Load ListFragment by default only if this is a fresh start
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
loadFragment(new ListFragment());
|
||||||
|
bottomNav.setSelectedItemId(R.id.nav_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
//when an item in the bar is selected, load the corresponding fragment
|
||||||
|
bottomNav.setOnItemSelectedListener(item -> {
|
||||||
|
|
||||||
|
if (item.getItemId() == R.id.nav_list) {
|
||||||
|
loadFragment(new ListFragment());
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.nav_chat) {
|
||||||
|
loadFragment(new ChatFragment());
|
||||||
|
return true;
|
||||||
|
} else if (item.getItemId() == R.id.nav_profile) {
|
||||||
|
loadFragment(new ProfileFragment());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to load a fragment
|
||||||
|
private void loadFragment(Fragment fragment) {
|
||||||
|
getSupportFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(R.id.fragment_container, fragment)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
package com.example.petstoremobile.activities;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.graphics.Insets;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.api.auth.AuthApi;
|
||||||
|
import com.example.petstoremobile.api.auth.TokenManager;
|
||||||
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
|
import com.example.petstoremobile.dtos.AuthDTO;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
//The login screen activity
|
||||||
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private EditText etUser;
|
||||||
|
private EditText etPassword;
|
||||||
|
private Button btnLogin;
|
||||||
|
private TextView tvLoginStatus;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Check if user is already logged in
|
||||||
|
if (TokenManager.getInstance(this).isLoggedIn()) {
|
||||||
|
Intent intent = new Intent(this, HomeActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
//get all controls from layout
|
||||||
|
tvLoginStatus = findViewById(R.id.tvLoginStatus);
|
||||||
|
etUser = findViewById(R.id.etUser);
|
||||||
|
etPassword = findViewById(R.id.etPassword);
|
||||||
|
btnLogin = findViewById(R.id.btnLogin);
|
||||||
|
//clear login status
|
||||||
|
tvLoginStatus.setText("");
|
||||||
|
|
||||||
|
//Set click listener for login button
|
||||||
|
btnLogin.setOnClickListener(v -> {
|
||||||
|
//Get username and password from text fields
|
||||||
|
String username = etUser.getText().toString();
|
||||||
|
String password = etPassword.getText().toString();
|
||||||
|
|
||||||
|
//check if fields are empty
|
||||||
|
if (username.isEmpty() || password.isEmpty()) {
|
||||||
|
Toast.makeText(this, "Please enter username and password", Toast.LENGTH_SHORT).show();
|
||||||
|
tvLoginStatus.setText("Please enter username and password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthApi authApi = RetrofitClient.getAuthApi(this);
|
||||||
|
|
||||||
|
//Call login from api and get response
|
||||||
|
authApi.login(new AuthDTO.LoginRequest(username,password)).enqueue(new Callback<AuthDTO.LoginResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<AuthDTO.LoginResponse> call, Response<AuthDTO.LoginResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
//save login data in shared preferences
|
||||||
|
TokenManager.getInstance(MainActivity.this).saveLoginData(
|
||||||
|
response.body().getToken(),
|
||||||
|
response.body().getUsername(),
|
||||||
|
response.body().getRole()
|
||||||
|
);
|
||||||
|
|
||||||
|
//fetch user id from api then login to home activity
|
||||||
|
RetrofitClient.getAuthApi(MainActivity.this).getCurrentUser()
|
||||||
|
.enqueue(new Callback<AuthDTO.UserResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<AuthDTO.UserResponse> call,
|
||||||
|
Response<AuthDTO.UserResponse> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
TokenManager.getInstance(MainActivity.this)
|
||||||
|
.saveUserId(response.body().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
|
||||||
|
startActivity(new Intent(MainActivity.this, HomeActivity.class));
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<AuthDTO.UserResponse> call,
|
||||||
|
Throwable t) {
|
||||||
|
Log.e("MainActivity", "Failed to fetch userId", t);
|
||||||
|
|
||||||
|
Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
|
||||||
|
startActivity(new Intent(MainActivity.this, HomeActivity.class));
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Toast.makeText(MainActivity.this, "Login failed", Toast.LENGTH_SHORT).show();
|
||||||
|
tvLoginStatus.setText("Login failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<AuthDTO.LoginResponse> call, Throwable t) {
|
||||||
|
Toast.makeText(MainActivity.this, "Login failed", Toast.LENGTH_SHORT).show();
|
||||||
|
tvLoginStatus.setText("Login failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.example.petstoremobile.adapters;
|
||||||
|
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.models.Adoption;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AdoptionAdapter extends RecyclerView.Adapter<AdoptionAdapter.AdoptionViewHolder> {
|
||||||
|
|
||||||
|
private List<Adoption> adoptionList;
|
||||||
|
private OnAdoptionClickListener adoptionClickListener;
|
||||||
|
|
||||||
|
// Interface for adoption click on recycler view
|
||||||
|
public interface OnAdoptionClickListener {
|
||||||
|
void onAdoptionClick(int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public AdoptionAdapter(List<Adoption> adoptionList, OnAdoptionClickListener adoptionClickListener) {
|
||||||
|
this.adoptionList = adoptionList;
|
||||||
|
this.adoptionClickListener = adoptionClickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the controls of each row in recycler view
|
||||||
|
public static class AdoptionViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvAdopterName, tvPetName, tvAdoptionDate, tvAdoptionStatus;
|
||||||
|
|
||||||
|
public AdoptionViewHolder(@NonNull View v) {
|
||||||
|
super(v);
|
||||||
|
tvAdopterName = v.findViewById(R.id.tvAdopterName);
|
||||||
|
tvPetName = v.findViewById(R.id.tvAdoptionPetName);
|
||||||
|
tvAdoptionDate = v.findViewById(R.id.tvAdoptionDate);
|
||||||
|
tvAdoptionStatus = v.findViewById(R.id.tvAdoptionStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new row view
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public AdoptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_adoption, parent, false);
|
||||||
|
return new AdoptionViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the row with adoption data
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull AdoptionViewHolder holder, int position) {
|
||||||
|
Adoption adoption = adoptionList.get(position);
|
||||||
|
|
||||||
|
holder.tvAdopterName.setText(adoption.getAdopterName());
|
||||||
|
holder.tvPetName.setText("Pet: " + adoption.getPetName());
|
||||||
|
holder.tvAdoptionDate.setText("Date: " + adoption.getAdoptionDate());
|
||||||
|
holder.tvAdoptionStatus.setText(adoption.getStatus());
|
||||||
|
|
||||||
|
// Set the status color depending on adoption status
|
||||||
|
if (adoption.getStatus().equals("Approved")) {
|
||||||
|
holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
|
||||||
|
} else if (adoption.getStatus().equals("Pending")) {
|
||||||
|
holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#FF9800"));
|
||||||
|
} else {
|
||||||
|
holder.tvAdoptionStatus.setBackgroundColor(Color.parseColor("#F44336"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a row is clicked, open the detail view
|
||||||
|
holder.itemView.setOnClickListener(v -> adoptionClickListener.onAdoptionClick(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return adoptionList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package com.example.petstoremobile.adapters;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.models.Appointment;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AppointmentAdapter extends RecyclerView.Adapter<AppointmentAdapter.AppointmentViewHolder> {
|
||||||
|
|
||||||
|
private List<Appointment> appointmentList;
|
||||||
|
private OnAppointmentClickListener appointmentClickListener;
|
||||||
|
|
||||||
|
// Interface for appointment click on recycler view
|
||||||
|
public interface OnAppointmentClickListener {
|
||||||
|
void onAppointmentClick(int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public AppointmentAdapter(List<Appointment> appointmentList, OnAppointmentClickListener appointmentClickListener) {
|
||||||
|
this.appointmentList = appointmentList;
|
||||||
|
this.appointmentClickListener = appointmentClickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the controls of each row in recycler view
|
||||||
|
public static class AppointmentViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvCustomerName, tvPetName, tvServiceType, tvDateTime, tvAppointmentStatus;
|
||||||
|
|
||||||
|
public AppointmentViewHolder(@NonNull View v) {
|
||||||
|
super(v);
|
||||||
|
tvCustomerName = v.findViewById(R.id.tvCustomerName);
|
||||||
|
tvPetName = v.findViewById(R.id.tvApptPetName);
|
||||||
|
tvServiceType = v.findViewById(R.id.tvServiceType);
|
||||||
|
tvDateTime = v.findViewById(R.id.tvDateTime);
|
||||||
|
tvAppointmentStatus = v.findViewById(R.id.tvAppointmentStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new row view
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public AppointmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_appointment, parent, false);
|
||||||
|
return new AppointmentViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the row with appointment data
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull AppointmentViewHolder holder, int position) {
|
||||||
|
Appointment appointment = appointmentList.get(position);
|
||||||
|
|
||||||
|
holder.tvCustomerName.setText(appointment.getCustomerName());
|
||||||
|
holder.tvPetName.setText("Pet: " + appointment.getPetName());
|
||||||
|
holder.tvServiceType.setText(appointment.getServiceType());
|
||||||
|
holder.tvDateTime.setText(appointment.getAppointmentDate() + " at " + appointment.getAppointmentTime());
|
||||||
|
holder.tvAppointmentStatus.setText(appointment.getStatus());
|
||||||
|
|
||||||
|
// Set the status color depending on appointment status
|
||||||
|
switch (appointment.getStatus()) {
|
||||||
|
case "Confirmed":
|
||||||
|
holder.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
|
||||||
|
break;
|
||||||
|
case "Pending":
|
||||||
|
holder.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#FF9800"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
holder.tvAppointmentStatus.setBackgroundColor(Color.parseColor("#F44336"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a row is clicked, open the detail view
|
||||||
|
holder.itemView.setOnClickListener(v -> appointmentClickListener.onAppointmentClick(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return appointmentList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.example.petstoremobile.adapters;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.models.Chat;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.ChatViewHolder> {
|
||||||
|
|
||||||
|
private List<Chat> chatList;
|
||||||
|
private OnChatClickListener listener;
|
||||||
|
|
||||||
|
public interface OnChatClickListener {
|
||||||
|
void onChatClick(Chat chat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatAdapter(List<Chat> chatList, OnChatClickListener listener) {
|
||||||
|
this.chatList = chatList;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ChatViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_chat, parent, false);
|
||||||
|
return new ChatViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ChatViewHolder holder, int position) {
|
||||||
|
Chat chat = chatList.get(position);
|
||||||
|
holder.tvCustomerName.setText(chat.getCustomerName());
|
||||||
|
holder.tvLastMessage.setText(chat.getLastMessage());
|
||||||
|
holder.itemView.setOnClickListener(v -> listener.onChatClick(chat));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return chatList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ChatViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvCustomerName, tvLastMessage;
|
||||||
|
|
||||||
|
public ChatViewHolder(@NonNull View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
tvCustomerName = itemView.findViewById(R.id.tvCustomerName);
|
||||||
|
tvLastMessage = itemView.findViewById(R.id.tvLastMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.example.petstoremobile.adapters;
|
||||||
|
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.models.Inventory;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InventoryAdapter extends RecyclerView.Adapter<InventoryAdapter.InventoryViewHolder> {
|
||||||
|
|
||||||
|
private List<Inventory> inventoryList;
|
||||||
|
private OnInventoryClickListener inventoryClickListener;
|
||||||
|
|
||||||
|
// Interface for inventory click on recycler view
|
||||||
|
public interface OnInventoryClickListener {
|
||||||
|
void onInventoryClick(int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public InventoryAdapter(List<Inventory> inventoryList, OnInventoryClickListener inventoryClickListener) {
|
||||||
|
this.inventoryList = inventoryList;
|
||||||
|
this.inventoryClickListener = inventoryClickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the controls of each row in recycler view
|
||||||
|
public static class InventoryViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvItemName, tvCategory, tvQuantity, tvUnitPrice, tvSupplier;
|
||||||
|
|
||||||
|
public InventoryViewHolder(@NonNull View v) {
|
||||||
|
super(v);
|
||||||
|
tvItemName = v.findViewById(R.id.tvItemName);
|
||||||
|
tvCategory = v.findViewById(R.id.tvCategory);
|
||||||
|
tvQuantity = v.findViewById(R.id.tvQuantity);
|
||||||
|
tvUnitPrice = v.findViewById(R.id.tvUnitPrice);
|
||||||
|
tvSupplier = v.findViewById(R.id.tvInvSupplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new row view
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public InventoryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_inventory, parent, false);
|
||||||
|
return new InventoryViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the row with inventory data
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull InventoryViewHolder holder, int position) {
|
||||||
|
Inventory inventory = inventoryList.get(position);
|
||||||
|
|
||||||
|
holder.tvItemName.setText(inventory.getItemName());
|
||||||
|
holder.tvCategory.setText(inventory.getCategory());
|
||||||
|
holder.tvQuantity.setText("Qty: " + inventory.getQuantity());
|
||||||
|
holder.tvUnitPrice.setText("$" + String.format("%.2f", inventory.getUnitPrice()));
|
||||||
|
holder.tvSupplier.setText("Supplier: " + inventory.getSupplier());
|
||||||
|
|
||||||
|
// Highlight low stock items in red
|
||||||
|
if (inventory.getQuantity() <= 5) {
|
||||||
|
holder.tvQuantity.setTextColor(Color.parseColor("#F44336"));
|
||||||
|
} else {
|
||||||
|
holder.tvQuantity.setTextColor(Color.parseColor("#4CAF50"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a row is clicked, open the detail view
|
||||||
|
holder.itemView.setOnClickListener(v -> inventoryClickListener.onInventoryClick(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return inventoryList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.example.petstoremobile.adapters;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.models.Message;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MessageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
|
private static final int TYPE_SENT = 1;
|
||||||
|
private static final int TYPE_RECEIVED = 2;
|
||||||
|
|
||||||
|
private final List<Message> messages;
|
||||||
|
private Long currentUserId;
|
||||||
|
|
||||||
|
public MessageAdapter(List<Message> messages, Long currentUserId) {
|
||||||
|
this.messages = messages;
|
||||||
|
this.currentUserId = currentUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCurrentUserId(Long id) {
|
||||||
|
this.currentUserId = id;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
Message m = messages.get(position);
|
||||||
|
if (currentUserId != null && currentUserId.equals(m.getSenderId())) {
|
||||||
|
return TYPE_SENT;
|
||||||
|
}
|
||||||
|
return TYPE_RECEIVED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull @Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
LayoutInflater inf = LayoutInflater.from(parent.getContext());
|
||||||
|
if (viewType == TYPE_SENT) {
|
||||||
|
View v = inf.inflate(R.layout.item_message_sent, parent, false);
|
||||||
|
return new SentHolder(v);
|
||||||
|
} else {
|
||||||
|
View v = inf.inflate(R.layout.item_message_received, parent, false);
|
||||||
|
return new ReceivedHolder(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
|
Message m = messages.get(position);
|
||||||
|
if (holder instanceof SentHolder) ((SentHolder) holder).bind(m);
|
||||||
|
if (holder instanceof ReceivedHolder) ((ReceivedHolder) holder).bind(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public int getItemCount() { return messages.size(); }
|
||||||
|
|
||||||
|
static class SentHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvMessage;
|
||||||
|
SentHolder(View v) {
|
||||||
|
super(v);
|
||||||
|
tvMessage = v.findViewById(R.id.tvMessageContent); // updated
|
||||||
|
}
|
||||||
|
void bind(Message m) { tvMessage.setText(m.getContent()); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ReceivedHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvMessage;
|
||||||
|
ReceivedHolder(View v) {
|
||||||
|
super(v);
|
||||||
|
tvMessage = v.findViewById(R.id.tvMessageContent); // updated
|
||||||
|
}
|
||||||
|
void bind(Message m) { tvMessage.setText(m.getContent()); }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.example.petstoremobile.adapters;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.dtos.PetDTO;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PetAdapter extends RecyclerView.Adapter<PetAdapter.PetViewHolder> {
|
||||||
|
|
||||||
|
private List<PetDTO> petList;
|
||||||
|
private OnPetClickListener petClickListener;
|
||||||
|
|
||||||
|
// Interface for pet click on recycler view
|
||||||
|
public interface OnPetClickListener {
|
||||||
|
void onPetClick(int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Constructor
|
||||||
|
public PetAdapter(List<PetDTO> petList, OnPetClickListener petClickListener) {
|
||||||
|
this.petList = petList;
|
||||||
|
this.petClickListener = petClickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the controls of each row in recycler view
|
||||||
|
public static class PetViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvPetName, tvPetSpeciesBreed, tvPetAge, tvPetPrice, tvPetStatus;
|
||||||
|
|
||||||
|
public PetViewHolder(@NonNull View v) {
|
||||||
|
super(v);
|
||||||
|
tvPetName = v.findViewById(R.id.tvPetName);
|
||||||
|
tvPetSpeciesBreed = v.findViewById(R.id.tvPetSpeciesBreed);
|
||||||
|
tvPetAge = v.findViewById(R.id.tvPetAge);
|
||||||
|
tvPetPrice = v.findViewById(R.id.tvPetPrice);
|
||||||
|
tvPetStatus = v.findViewById(R.id.tvPetStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new row view
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public PetViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_pet, parent, false);
|
||||||
|
return new PetViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
//populate the row with pet data
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull PetViewHolder holder, int position) {
|
||||||
|
PetDTO pet = petList.get(position);
|
||||||
|
|
||||||
|
holder.tvPetName.setText(pet.getPetName());
|
||||||
|
holder.tvPetSpeciesBreed.setText(pet.getPetSpecies() + " - " + pet.getPetBreed());
|
||||||
|
holder.tvPetAge.setText("Age: " + pet.getPetAge() + " yr(s)");
|
||||||
|
|
||||||
|
try {
|
||||||
|
double price = Double.parseDouble(pet.getPetPrice());
|
||||||
|
holder.tvPetPrice.setText("$" + String.format("%.2f", price));
|
||||||
|
} catch (Exception e) {
|
||||||
|
holder.tvPetPrice.setText("$" + pet.getPetPrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.tvPetStatus.setText(pet.getPetStatus());
|
||||||
|
|
||||||
|
//Set the status color depending on availability. If available, green, otherwise red
|
||||||
|
if (pet.getPetStatus() != null && pet.getPetStatus().equals("Available")) {
|
||||||
|
holder.tvPetStatus.setBackgroundColor(Color.parseColor("#4CAF50"));
|
||||||
|
} else {
|
||||||
|
holder.tvPetStatus.setBackgroundColor(Color.parseColor("#F44336"));
|
||||||
|
}
|
||||||
|
|
||||||
|
//when a row is clicked, open the detail view
|
||||||
|
holder.itemView.setOnClickListener(v -> petClickListener.onPetClick(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return petList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.example.petstoremobile.adapters;
|
||||||
|
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.models.Product;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ProductViewHolder> {
|
||||||
|
|
||||||
|
private List<Product> productList;
|
||||||
|
private OnProductClickListener productClickListener;
|
||||||
|
|
||||||
|
// Interface for product click on recycler view
|
||||||
|
public interface OnProductClickListener {
|
||||||
|
void onProductClick(int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public ProductAdapter(List<Product> productList, OnProductClickListener productClickListener) {
|
||||||
|
this.productList = productList;
|
||||||
|
this.productClickListener = productClickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the controls of each row in recycler view
|
||||||
|
public static class ProductViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvProductName, tvProductDesc, tvCategory, tvProductPrice, tvStockQuantity;
|
||||||
|
|
||||||
|
public ProductViewHolder(@NonNull View v) {
|
||||||
|
super(v);
|
||||||
|
tvProductName = v.findViewById(R.id.tvProductName);
|
||||||
|
tvProductDesc = v.findViewById(R.id.tvProductDesc);
|
||||||
|
tvCategory = v.findViewById(R.id.tvProductCategory);
|
||||||
|
tvProductPrice = v.findViewById(R.id.tvProductPrice);
|
||||||
|
tvStockQuantity = v.findViewById(R.id.tvStockQuantity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new row view
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ProductViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_product, parent, false);
|
||||||
|
return new ProductViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the row with product data
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ProductViewHolder holder, int position) {
|
||||||
|
Product product = productList.get(position);
|
||||||
|
|
||||||
|
holder.tvProductName.setText(product.getProductName());
|
||||||
|
holder.tvProductDesc.setText(product.getProductDesc());
|
||||||
|
holder.tvCategory.setText(product.getCategory());
|
||||||
|
holder.tvProductPrice.setText("$" + String.format("%.2f", product.getProductPrice()));
|
||||||
|
holder.tvStockQuantity.setText("Stock: " + product.getStockQuantity());
|
||||||
|
|
||||||
|
// When a row is clicked, open the detail view
|
||||||
|
holder.itemView.setOnClickListener(v -> productClickListener.onProductClick(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return productList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.example.petstoremobile.adapters;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ServiceAdapter extends RecyclerView.Adapter<ServiceAdapter.ServiceViewHolder> {
|
||||||
|
|
||||||
|
private List<ServiceDTO> serviceList;
|
||||||
|
private OnServiceClickListener serviceClickListener;
|
||||||
|
|
||||||
|
// Interface for service click on recycler view
|
||||||
|
public interface OnServiceClickListener {
|
||||||
|
void onServiceClick(int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Constructor
|
||||||
|
public ServiceAdapter(List<ServiceDTO> serviceList, OnServiceClickListener serviceClickListener) {
|
||||||
|
this.serviceList = serviceList;
|
||||||
|
this.serviceClickListener = serviceClickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the controls of each row in recycler view
|
||||||
|
public static class ServiceViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvServiceName, tvServiceDesc, tvServiceDuration, tvServicePrice;
|
||||||
|
|
||||||
|
public ServiceViewHolder(@NonNull View v) {
|
||||||
|
super(v);
|
||||||
|
tvServiceName = v.findViewById(R.id.tvServiceName);
|
||||||
|
tvServiceDesc = v.findViewById(R.id.tvServiceDesc);
|
||||||
|
tvServiceDuration = v.findViewById(R.id.tvServiceDuration);
|
||||||
|
tvServicePrice = v.findViewById(R.id.tvServicePrice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new row view
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ServiceViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_service, parent, false);
|
||||||
|
return new ServiceViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
//populate the row with service data
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ServiceViewHolder holder, int position) {
|
||||||
|
ServiceDTO service = serviceList.get(position);
|
||||||
|
|
||||||
|
holder.tvServiceName.setText(service.getServiceName());
|
||||||
|
holder.tvServiceDesc.setText(service.getServiceDesc());
|
||||||
|
holder.tvServiceDuration.setText("Duration: " + service.getServiceDuration() + " min");
|
||||||
|
holder.tvServicePrice.setText("$" + String.format("%.2f", service.getServicePrice()));
|
||||||
|
|
||||||
|
//when a row is clicked, open the detail view
|
||||||
|
holder.itemView.setOnClickListener(v -> serviceClickListener.onServiceClick(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return serviceList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package com.example.petstoremobile.adapters;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.dtos.SupplierDTO;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SupplierAdapter extends RecyclerView.Adapter<SupplierAdapter.SupplierViewHolder> {
|
||||||
|
|
||||||
|
private List<SupplierDTO> supplierList;
|
||||||
|
private OnSupplierClickListener supplierClickListener;
|
||||||
|
|
||||||
|
// Interface for supplier click on recycler view
|
||||||
|
public interface OnSupplierClickListener {
|
||||||
|
void onSupplierClick(int position);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Constructor
|
||||||
|
public SupplierAdapter(List<SupplierDTO> supplierList, OnSupplierClickListener supplierClickListener) {
|
||||||
|
this.supplierList = supplierList;
|
||||||
|
this.supplierClickListener = supplierClickListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the controls of each row in recycler view
|
||||||
|
public static class SupplierViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
TextView tvSupCompany, tvSupContactName, tvSupEmail, tvSupPhone;
|
||||||
|
|
||||||
|
public SupplierViewHolder(@NonNull View v) {
|
||||||
|
super(v);
|
||||||
|
tvSupCompany = v.findViewById(R.id.tvSupCompany);
|
||||||
|
tvSupContactName = v.findViewById(R.id.tvSupContactName);
|
||||||
|
tvSupEmail = v.findViewById(R.id.tvSupEmail);
|
||||||
|
tvSupPhone = v.findViewById(R.id.tvSupPhone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new row view
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public SupplierViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_supplier, parent, false);
|
||||||
|
return new SupplierViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
//populate the row with supplier data
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull SupplierViewHolder holder, int position) {
|
||||||
|
SupplierDTO supplier = supplierList.get(position);
|
||||||
|
|
||||||
|
holder.tvSupCompany.setText(supplier.getSupCompany());
|
||||||
|
holder.tvSupContactName.setText(supplier.getSupContactFirstName() + " " + supplier.getSupContactLastName());
|
||||||
|
holder.tvSupEmail.setText(supplier.getSupEmail());
|
||||||
|
holder.tvSupPhone.setText(supplier.getSupPhone());
|
||||||
|
|
||||||
|
//when a row is clicked, open the detail view
|
||||||
|
holder.itemView.setOnClickListener(v -> supplierClickListener.onSupplierClick(position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return supplierList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.example.petstoremobile.api;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.dtos.ConversationDTO;
|
||||||
|
import com.example.petstoremobile.dtos.MessageDTO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.Body;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
|
||||||
|
//api calls to get conversations
|
||||||
|
public interface ChatApi {
|
||||||
|
|
||||||
|
@GET("api/v1/chat/conversations")
|
||||||
|
Call<List<ConversationDTO>> getAllConversations();
|
||||||
|
|
||||||
|
@GET("api/v1/chat/conversations/{conversationId}")
|
||||||
|
Call<ConversationDTO> getConversationById(@Path("conversationId") Long conversationId);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.example.petstoremobile.api;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.dtos.CustomerDTO;
|
||||||
|
import com.example.petstoremobile.dtos.PageResponse;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
import retrofit2.http.Query;
|
||||||
|
|
||||||
|
//api calls to get customers
|
||||||
|
public interface CustomerApi {
|
||||||
|
|
||||||
|
@GET("api/v1/customers")
|
||||||
|
Call<PageResponse<CustomerDTO>> getAllCustomers(@Query("page") int page, @Query("size") int size);
|
||||||
|
|
||||||
|
@GET("api/v1/customers/{customerId}")
|
||||||
|
Call<CustomerDTO> getCustomerById(@Path("customerId") Long customerId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.example.petstoremobile.api;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.dtos.MessageDTO;
|
||||||
|
import com.example.petstoremobile.dtos.SendMessageRequest;
|
||||||
|
import java.util.List;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.Body;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
import retrofit2.http.Path;
|
||||||
|
|
||||||
|
//api calls to get and send messages
|
||||||
|
public interface MessageApi {
|
||||||
|
|
||||||
|
@GET("api/v1/chat/conversations/{id}/messages")
|
||||||
|
Call<List<MessageDTO>> getMessages(@Path("id") Long conversationId);
|
||||||
|
|
||||||
|
@POST("api/v1/chat/conversations/{id}/messages")
|
||||||
|
Call<MessageDTO> sendMessage(@Path("id") Long conversationId, @Body SendMessageRequest request);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.example.petstoremobile.api;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.dtos.PageResponse;
|
||||||
|
import com.example.petstoremobile.dtos.PetDTO;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
//api calls to CRUD pets
|
||||||
|
public interface PetApi {
|
||||||
|
// Get all pets
|
||||||
|
@GET("api/v1/pets")
|
||||||
|
Call<PageResponse<PetDTO>> getAllPets(
|
||||||
|
@Query("page") int page,
|
||||||
|
@Query("size") int size
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get pet by id
|
||||||
|
@GET("api/v1/pets/{id}")
|
||||||
|
Call<PetDTO> getPetById(@Path("id") Long id);
|
||||||
|
|
||||||
|
// Create pet
|
||||||
|
@POST("api/v1/pets")
|
||||||
|
Call<PetDTO> createPet(@Body PetDTO pet);
|
||||||
|
|
||||||
|
// Update pet
|
||||||
|
@PUT("api/v1/pets/{id}")
|
||||||
|
Call<PetDTO> updatePet(@Path("id") Long id, @Body PetDTO pet);
|
||||||
|
|
||||||
|
// Delete pet
|
||||||
|
@DELETE("api/v1/pets/{id}")
|
||||||
|
Call<Void> deletePet(@Path("id") Long id);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.example.petstoremobile.api;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.api.auth.AuthApi;
|
||||||
|
import com.example.petstoremobile.api.auth.AuthInterceptor;
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
|
import retrofit2.Retrofit;
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
|
|
||||||
|
//Retrofit client Used for API calls
|
||||||
|
public class RetrofitClient {
|
||||||
|
//base URL
|
||||||
|
public static final String BASE_URL = "http://10.0.2.2:8080"; //for emulator testing
|
||||||
|
// public static final String BASE_URL = "http://10.0.0.200:8080/"; //for hardware testing
|
||||||
|
|
||||||
|
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(context))
|
||||||
|
.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 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.example.petstoremobile.api;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.dtos.PageResponse;
|
||||||
|
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
//api calls to CRUD services
|
||||||
|
public interface ServiceApi {
|
||||||
|
// Get all services
|
||||||
|
@GET("api/v1/services")
|
||||||
|
Call<PageResponse<ServiceDTO>> getAllServices(
|
||||||
|
@Query("page") int page,
|
||||||
|
@Query("size") int size
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get service by id
|
||||||
|
@GET("api/v1/services/{id}")
|
||||||
|
Call<ServiceDTO> getServiceById(@Path("id") Long id);
|
||||||
|
|
||||||
|
// Create service
|
||||||
|
@POST("api/v1/services")
|
||||||
|
Call<ServiceDTO> createService(@Body ServiceDTO service);
|
||||||
|
|
||||||
|
// Update service
|
||||||
|
@PUT("api/v1/services/{id}")
|
||||||
|
Call<ServiceDTO> updateService(@Path("id") Long id, @Body ServiceDTO service);
|
||||||
|
|
||||||
|
// Delete service
|
||||||
|
@DELETE("api/v1/services/{id}")
|
||||||
|
Call<Void> deleteService(@Path("id") Long id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.example.petstoremobile.api;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.dtos.PageResponse;
|
||||||
|
import com.example.petstoremobile.dtos.SupplierDTO;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
//api calls to CRUD suppliers
|
||||||
|
public interface SupplierApi {
|
||||||
|
// Get all suppliers
|
||||||
|
@GET("api/v1/suppliers")
|
||||||
|
Call<PageResponse<SupplierDTO>> getAllSuppliers(
|
||||||
|
@Query("page") int page,
|
||||||
|
@Query("size") int size
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get supplier by id
|
||||||
|
@GET("api/v1/suppliers/{id}")
|
||||||
|
Call<SupplierDTO> getSupplierById(@Path("id") Long id);
|
||||||
|
|
||||||
|
// Create supplier
|
||||||
|
@POST("api/v1/suppliers")
|
||||||
|
Call<SupplierDTO> createSupplier(@Body SupplierDTO supplier);
|
||||||
|
|
||||||
|
// Update supplier
|
||||||
|
@PUT("api/v1/suppliers/{id}")
|
||||||
|
Call<SupplierDTO> updateSupplier(@Path("id") Long id, @Body SupplierDTO supplier);
|
||||||
|
|
||||||
|
// Delete supplier
|
||||||
|
@DELETE("api/v1/suppliers/{id}")
|
||||||
|
Call<Void> deleteSupplier(@Path("id") Long id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.example.petstoremobile.api.auth;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.dtos.AuthDTO;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.Body;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
|
||||||
|
//Api for logging in and getting current user
|
||||||
|
public interface AuthApi {
|
||||||
|
@POST("api/v1/auth/login")
|
||||||
|
Call<AuthDTO.LoginResponse> login(@Body AuthDTO.LoginRequest loginRequest);
|
||||||
|
|
||||||
|
@GET("api/v1/auth/me")
|
||||||
|
Call<AuthDTO.UserResponse> getCurrentUser();
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.example.petstoremobile.api.auth;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import okhttp3.Interceptor;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
|
//Used to get the token from the backend for authenticated api calls
|
||||||
|
public class AuthInterceptor implements Interceptor {
|
||||||
|
|
||||||
|
private final TokenManager tokenManager;
|
||||||
|
|
||||||
|
public AuthInterceptor(Context context) {
|
||||||
|
this.tokenManager = TokenManager.getInstance(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Response intercept(@NonNull Chain chain) throws IOException {
|
||||||
|
String token = tokenManager.getToken();
|
||||||
|
String url = chain.request().url().toString();
|
||||||
|
|
||||||
|
if (url.contains("auth/login") || url.contains("auth/register")) {
|
||||||
|
return chain.proceed(chain.request());
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we have a token then add it to the request
|
||||||
|
if (token != null) {
|
||||||
|
Request request = chain.request().newBuilder()
|
||||||
|
.addHeader("Authorization", "Bearer " + token)
|
||||||
|
.build();
|
||||||
|
return chain.proceed(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
//If no token then just pass the request
|
||||||
|
return chain.proceed(chain.request());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package com.example.petstoremobile.api.auth;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
//Store login token in shared preferences
|
||||||
|
public class TokenManager {
|
||||||
|
private static final String TOKEN_KEY = "token";
|
||||||
|
private static final String USERNAME_KEY = "username";
|
||||||
|
private static final String ROLE_KEY = "role";
|
||||||
|
private static final String PREFS_NAME = "auth_prefs";
|
||||||
|
private static final String USER_ID_KEY = "user_id";
|
||||||
|
|
||||||
|
private static TokenManager instance;
|
||||||
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
|
private TokenManager(Context context) {
|
||||||
|
prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TokenManager getInstance(Context context) {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new TokenManager(context);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
//save login data after login
|
||||||
|
public void saveLoginData(String token, String username, String role) {
|
||||||
|
prefs.edit()
|
||||||
|
.putString(TOKEN_KEY, token)
|
||||||
|
.putString(USERNAME_KEY, username)
|
||||||
|
.putString(ROLE_KEY, role)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Getters
|
||||||
|
public String getToken() {
|
||||||
|
return prefs.getString(TOKEN_KEY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return prefs.getString(USERNAME_KEY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRole() {
|
||||||
|
return prefs.getString(ROLE_KEY, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getUserId() {
|
||||||
|
long id = prefs.getLong(USER_ID_KEY, -1L);
|
||||||
|
return id == -1L ? null : id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveUserId(Long userId) {
|
||||||
|
prefs.edit().putLong(USER_ID_KEY, userId).apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check if logged in
|
||||||
|
public boolean isLoggedIn() {
|
||||||
|
return getToken() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Clear login data
|
||||||
|
public void clearLoginData() {
|
||||||
|
prefs.edit().clear().apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
//Used to send login data to the backend
|
||||||
|
public class AuthDTO {
|
||||||
|
public static class LoginRequest {
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
public LoginRequest(String username, String password) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() { return username; }
|
||||||
|
public String getPassword() { return password; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//Used to receive login data from the backend
|
||||||
|
public static class LoginResponse {
|
||||||
|
private String token;
|
||||||
|
private String username;
|
||||||
|
private String role;
|
||||||
|
|
||||||
|
public String getToken() { return token; }
|
||||||
|
public String getUsername() { return username; }
|
||||||
|
public String getRole() { return role; }
|
||||||
|
}
|
||||||
|
|
||||||
|
//Used to get logged in profile
|
||||||
|
public static class UserResponse {
|
||||||
|
private Long id;
|
||||||
|
private String username;
|
||||||
|
private String email;
|
||||||
|
private String fullName;
|
||||||
|
private String avatarUrl;
|
||||||
|
private String role;
|
||||||
|
private Long storeId;
|
||||||
|
private String storeName;
|
||||||
|
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public String getUsername() { return username; }
|
||||||
|
public String getEmail() { return email; }
|
||||||
|
public String getFullName() { return fullName; }
|
||||||
|
public String getAvatarUrl() { return avatarUrl; }
|
||||||
|
public String getRole() { return role; }
|
||||||
|
public Long getStoreId() { return storeId; }
|
||||||
|
public String getStoreName() { return storeName; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
public class ConversationDTO {
|
||||||
|
private Long id;
|
||||||
|
private Long customerId;
|
||||||
|
private Long staffId;
|
||||||
|
private String status;
|
||||||
|
private String mode;
|
||||||
|
private String lastMessage;
|
||||||
|
private String createdAt;
|
||||||
|
private String updatedAt;
|
||||||
|
|
||||||
|
public ConversationDTO() {}
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCustomerId() {
|
||||||
|
return customerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomerId(Long customerId) {
|
||||||
|
this.customerId = customerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getStaffId() {
|
||||||
|
return staffId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStaffId(Long staffId) {
|
||||||
|
this.staffId = staffId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMode(String mode) {
|
||||||
|
this.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastMessage() {
|
||||||
|
return lastMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastMessage(String lastMessage) {
|
||||||
|
this.lastMessage = lastMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class CustomerDTO {
|
||||||
|
@SerializedName("customerId")
|
||||||
|
private Long customerId;
|
||||||
|
|
||||||
|
private String firstName;
|
||||||
|
private String lastName;
|
||||||
|
private String email;
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
public CustomerDTO() {}
|
||||||
|
|
||||||
|
public Long getCustomerId() {
|
||||||
|
return customerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomerId(Long customerId) {
|
||||||
|
this.customerId = customerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFirstName() {
|
||||||
|
return firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
this.firstName = firstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastName() {
|
||||||
|
return lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
this.lastName = lastName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPhone() {
|
||||||
|
return phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhone(String phone) {
|
||||||
|
this.phone = phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFullName() {
|
||||||
|
return (firstName != null ? firstName : "") + " " + (lastName != null ? lastName : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class MessageDTO {
|
||||||
|
|
||||||
|
@SerializedName("id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@SerializedName("conversationId")
|
||||||
|
private Long conversationId;
|
||||||
|
|
||||||
|
@SerializedName("senderId")
|
||||||
|
private Long senderId;
|
||||||
|
|
||||||
|
@SerializedName("content")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@SerializedName("timestamp")
|
||||||
|
private String timestamp;
|
||||||
|
|
||||||
|
@SerializedName("isRead")
|
||||||
|
private Boolean isRead;
|
||||||
|
|
||||||
|
public MessageDTO() {}
|
||||||
|
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
|
||||||
|
public Long getConversationId() { return conversationId; }
|
||||||
|
public void setConversationId(Long conversationId) { this.conversationId = conversationId; }
|
||||||
|
|
||||||
|
public Long getSenderId() { return senderId; }
|
||||||
|
public void setSenderId(Long senderId) { this.senderId = senderId; }
|
||||||
|
|
||||||
|
public String getContent() { return content; }
|
||||||
|
public void setContent(String content) { this.content = content; }
|
||||||
|
|
||||||
|
public String getTimestamp() { return timestamp; }
|
||||||
|
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
|
||||||
|
|
||||||
|
public Boolean getIsRead() { return isRead; }
|
||||||
|
public void setIsRead(Boolean isRead) { this.isRead = isRead; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
//Used to get data from the API
|
||||||
|
public class PageResponse<T> {
|
||||||
|
private List<T> content;
|
||||||
|
private int totalPages;
|
||||||
|
private long totalElements;
|
||||||
|
private int number;
|
||||||
|
private int size;
|
||||||
|
private boolean last;
|
||||||
|
|
||||||
|
public List<T> getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
public int getTotalPages() {
|
||||||
|
return totalPages;
|
||||||
|
}
|
||||||
|
public long getTotalElements() {
|
||||||
|
return totalElements;
|
||||||
|
}
|
||||||
|
public int getNumber() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
public boolean isLast() {
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
public class PetDTO {
|
||||||
|
private Long petId;
|
||||||
|
private String petName;
|
||||||
|
private String petSpecies;
|
||||||
|
private String petBreed;
|
||||||
|
private Integer petAge;
|
||||||
|
private String petStatus;
|
||||||
|
private String petPrice;
|
||||||
|
private String createdAt;
|
||||||
|
private String updatedAt;
|
||||||
|
|
||||||
|
public Long getPetId() { return petId; }
|
||||||
|
public void setPetId(Long petId) { this.petId = petId; }
|
||||||
|
|
||||||
|
public String getPetName() { return petName; }
|
||||||
|
public void setPetName(String petName) { this.petName = petName; }
|
||||||
|
|
||||||
|
public String getPetSpecies() { return petSpecies; }
|
||||||
|
public void setPetSpecies(String petSpecies) { this.petSpecies = petSpecies; }
|
||||||
|
|
||||||
|
public String getPetBreed() { return petBreed; }
|
||||||
|
public void setPetBreed(String petBreed) { this.petBreed = petBreed; }
|
||||||
|
|
||||||
|
public Integer getPetAge() { return petAge; }
|
||||||
|
public void setPetAge(Integer petAge) { this.petAge = petAge; }
|
||||||
|
|
||||||
|
public String getPetStatus() { return petStatus; }
|
||||||
|
public void setPetStatus(String petStatus) { this.petStatus = petStatus; }
|
||||||
|
|
||||||
|
public String getPetPrice() { return petPrice; }
|
||||||
|
public void setPetPrice(String petPrice) { this.petPrice = petPrice; }
|
||||||
|
|
||||||
|
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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
public class SendMessageRequest {
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
public SendMessageRequest(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() { return content; }
|
||||||
|
public void setContent(String content) { this.content = content; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
public class ServiceDTO {
|
||||||
|
private Long serviceId;
|
||||||
|
private String serviceName;
|
||||||
|
private String serviceDesc;
|
||||||
|
private Integer serviceDuration;
|
||||||
|
private Double servicePrice;
|
||||||
|
private String createdAt;
|
||||||
|
private String updatedAt;
|
||||||
|
|
||||||
|
public Long getServiceId() { return serviceId; }
|
||||||
|
public void setServiceId(Long serviceId) { this.serviceId = serviceId; }
|
||||||
|
|
||||||
|
public String getServiceName() { return serviceName; }
|
||||||
|
public void setServiceName(String serviceName) { this.serviceName = serviceName; }
|
||||||
|
|
||||||
|
public String getServiceDesc() { return serviceDesc; }
|
||||||
|
public void setServiceDesc(String serviceDesc) { this.serviceDesc = serviceDesc; }
|
||||||
|
|
||||||
|
public Integer getServiceDuration() { return serviceDuration; }
|
||||||
|
public void setServiceDuration(Integer serviceDuration) { this.serviceDuration = serviceDuration; }
|
||||||
|
|
||||||
|
public Double getServicePrice() { return servicePrice; }
|
||||||
|
public void setServicePrice(Double servicePrice) { this.servicePrice = servicePrice; }
|
||||||
|
|
||||||
|
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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.example.petstoremobile.dtos;
|
||||||
|
|
||||||
|
public class SupplierDTO {
|
||||||
|
private Long supId;
|
||||||
|
private String supCompany;
|
||||||
|
private String supContactFirstName;
|
||||||
|
private String supContactLastName;
|
||||||
|
private String supEmail;
|
||||||
|
private String supPhone;
|
||||||
|
private String createdAt;
|
||||||
|
private String updatedAt;
|
||||||
|
|
||||||
|
public Long getSupId() { return supId; }
|
||||||
|
public void setSupId(Long supId) { this.supId = supId; }
|
||||||
|
|
||||||
|
public String getSupCompany() { return supCompany; }
|
||||||
|
public void setSupCompany(String supCompany) { this.supCompany = supCompany; }
|
||||||
|
|
||||||
|
public String getSupContactFirstName() { return supContactFirstName; }
|
||||||
|
public void setSupContactFirstName(String supContactFirstName) { this.supContactFirstName = supContactFirstName; }
|
||||||
|
|
||||||
|
public String getSupContactLastName() { return supContactLastName; }
|
||||||
|
public void setSupContactLastName(String supContactLastName) { this.supContactLastName = supContactLastName; }
|
||||||
|
|
||||||
|
public String getSupEmail() { return supEmail; }
|
||||||
|
public void setSupEmail(String supEmail) { this.supEmail = supEmail; }
|
||||||
|
|
||||||
|
public String getSupPhone() { return supPhone; }
|
||||||
|
public void setSupPhone(String supPhone) { this.supPhone = supPhone; }
|
||||||
|
|
||||||
|
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; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,407 @@
|
|||||||
|
package com.example.petstoremobile.fragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.view.GravityCompat;
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.adapters.ChatAdapter;
|
||||||
|
import com.example.petstoremobile.adapters.MessageAdapter;
|
||||||
|
import com.example.petstoremobile.api.auth.TokenManager;
|
||||||
|
import com.example.petstoremobile.api.ChatApi;
|
||||||
|
import com.example.petstoremobile.api.CustomerApi;
|
||||||
|
import com.example.petstoremobile.api.MessageApi;
|
||||||
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
|
import com.example.petstoremobile.dtos.ConversationDTO;
|
||||||
|
import com.example.petstoremobile.dtos.CustomerDTO;
|
||||||
|
import com.example.petstoremobile.dtos.MessageDTO;
|
||||||
|
import com.example.petstoremobile.dtos.PageResponse;
|
||||||
|
import com.example.petstoremobile.dtos.SendMessageRequest;
|
||||||
|
import com.example.petstoremobile.models.Chat;
|
||||||
|
import com.example.petstoremobile.models.Message;
|
||||||
|
import com.example.petstoremobile.websocket.StompChatManager;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import retrofit2.*;
|
||||||
|
|
||||||
|
public class ChatFragment extends Fragment implements ChatAdapter.OnChatClickListener, StompChatManager.MessageListener,
|
||||||
|
StompChatManager.ConversationListener, StompChatManager.ConnectionListener {
|
||||||
|
|
||||||
|
private static final String TAG = "ChatFragment";
|
||||||
|
|
||||||
|
// View
|
||||||
|
private DrawerLayout drawerLayout;
|
||||||
|
private RecyclerView rvChatList, rvMessages;
|
||||||
|
private EditText etMessage;
|
||||||
|
private Button btnSend;
|
||||||
|
|
||||||
|
// Adapters
|
||||||
|
private ChatAdapter chatAdapter;
|
||||||
|
private MessageAdapter messageAdapter;
|
||||||
|
|
||||||
|
// Data
|
||||||
|
private final List<Chat> chatList = new ArrayList<>();
|
||||||
|
private final List<Message> messageList = new ArrayList<>();
|
||||||
|
private final Map<Long, String> customerNames = new HashMap<>();
|
||||||
|
|
||||||
|
// APIs
|
||||||
|
private ChatApi chatApi;
|
||||||
|
private CustomerApi customerApi;
|
||||||
|
private MessageApi messageApi;
|
||||||
|
|
||||||
|
// chat
|
||||||
|
private Long currentUserId;
|
||||||
|
private Long activeConversationId;
|
||||||
|
private StompChatManager stompChatManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
View view = inflater.inflate(R.layout.fragment_chat, container, false);
|
||||||
|
|
||||||
|
chatApi = RetrofitClient.getChatApi(requireContext());
|
||||||
|
customerApi = RetrofitClient.getCustomerApi(requireContext());
|
||||||
|
messageApi = RetrofitClient.getMessageApi(requireContext());
|
||||||
|
|
||||||
|
drawerLayout = view.findViewById(R.id.chatDrawerLayout);
|
||||||
|
rvChatList = view.findViewById(R.id.rvChatList);
|
||||||
|
rvMessages = view.findViewById(R.id.rvMessages);
|
||||||
|
etMessage = view.findViewById(R.id.etMessage);
|
||||||
|
btnSend = view.findViewById(R.id.btnSend);
|
||||||
|
|
||||||
|
ImageButton hamburger = view.findViewById(R.id.btnHamburger);
|
||||||
|
hamburger.setOnClickListener(v -> drawerLayout.openDrawer(GravityCompat.START));
|
||||||
|
btnSend.setOnClickListener(v -> sendMessage());
|
||||||
|
|
||||||
|
setupRecyclerViews();
|
||||||
|
loadInitialData();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRecyclerViews() {
|
||||||
|
// Set up Drawer menu to select conversation
|
||||||
|
chatAdapter = new ChatAdapter(chatList, this);
|
||||||
|
rvChatList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
rvChatList.setAdapter(chatAdapter);
|
||||||
|
|
||||||
|
// set up RecyclerView for selected chat to show messages
|
||||||
|
messageAdapter = new MessageAdapter(messageList, null);
|
||||||
|
LinearLayoutManager lm = new LinearLayoutManager(getContext());
|
||||||
|
lm.setStackFromEnd(true);
|
||||||
|
rvMessages.setLayoutManager(lm);
|
||||||
|
rvMessages.setAdapter(messageAdapter);
|
||||||
|
setConversationActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper function to load token and user id then connect to websocket
|
||||||
|
private void loadInitialData() {
|
||||||
|
TokenManager tm = TokenManager.getInstance(requireContext());
|
||||||
|
String token = tm.getToken();
|
||||||
|
currentUserId = tm.getUserId();
|
||||||
|
String role = tm.getRole();
|
||||||
|
|
||||||
|
messageAdapter.setCurrentUserId(currentUserId);
|
||||||
|
|
||||||
|
// if token exist then connect to websocket
|
||||||
|
if (token != null) {
|
||||||
|
stompChatManager = new StompChatManager(token, role);
|
||||||
|
stompChatManager.setMessageListener(this);
|
||||||
|
stompChatManager.setConversationListener(this);
|
||||||
|
stompChatManager.setConnectionListener(this);
|
||||||
|
stompChatManager.connect();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "No token found");
|
||||||
|
}
|
||||||
|
|
||||||
|
loadCustomers();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper function to load customer names for it to be displayed on drawer menu
|
||||||
|
private void loadCustomers() {
|
||||||
|
customerApi.getAllCustomers(0, 100).enqueue(new Callback<PageResponse<CustomerDTO>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<PageResponse<CustomerDTO>> call,
|
||||||
|
@NonNull Response<PageResponse<CustomerDTO>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
for (CustomerDTO c : response.body().getContent()) {
|
||||||
|
customerNames.put(c.getCustomerId(), c.getFullName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadConversations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<PageResponse<CustomerDTO>> call,
|
||||||
|
@NonNull Throwable t) {
|
||||||
|
loadConversations();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to load conversations entities to display with customer names in drawer menu
|
||||||
|
private void loadConversations() {
|
||||||
|
chatApi.getAllConversations().enqueue(new Callback<List<ConversationDTO>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<List<ConversationDTO>> call,
|
||||||
|
@NonNull Response<List<ConversationDTO>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
chatList.clear();
|
||||||
|
List<Chat> loaded = response.body().stream()
|
||||||
|
.map(dto -> {
|
||||||
|
String name = customerNames.getOrDefault(
|
||||||
|
dto.getCustomerId(), "Customer #" + dto.getCustomerId());
|
||||||
|
return new Chat(String.valueOf(dto.getId()),
|
||||||
|
name, dto.getLastMessage(),
|
||||||
|
dto.getCustomerId(), dto.getStaffId());
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
chatList.addAll(loaded);
|
||||||
|
chatAdapter.notifyDataSetChanged();
|
||||||
|
if (activeConversationId == null) {
|
||||||
|
messageList.clear();
|
||||||
|
messageAdapter.notifyDataSetChanged();
|
||||||
|
setConversationActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<List<ConversationDTO>> call,
|
||||||
|
@NonNull Throwable t) {
|
||||||
|
Log.e(TAG, "Error loading conversations", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when user taps a chat in the drawer
|
||||||
|
// Loads messages for that chat selected
|
||||||
|
@Override
|
||||||
|
public void onChatClick(Chat chat) {
|
||||||
|
activeConversationId = Long.parseLong(chat.getChatId());
|
||||||
|
setConversationActive(true);
|
||||||
|
drawerLayout.closeDrawer(GravityCompat.START);
|
||||||
|
|
||||||
|
if (stompChatManager != null) {
|
||||||
|
stompChatManager.subscribeToConversation(activeConversationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadMessageHistory(activeConversationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to load messages for selected chat
|
||||||
|
private void loadMessageHistory(Long conversationId) {
|
||||||
|
messageApi.getMessages(conversationId).enqueue(new Callback<List<MessageDTO>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<List<MessageDTO>> call,
|
||||||
|
@NonNull Response<List<MessageDTO>> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
messageList.clear();
|
||||||
|
for (MessageDTO dto : response.body()) {
|
||||||
|
messageList.add(dtoToModel(dto));
|
||||||
|
}
|
||||||
|
messageAdapter.notifyDataSetChanged();
|
||||||
|
scrollToBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<List<MessageDTO>> call,
|
||||||
|
@NonNull Throwable t) {
|
||||||
|
Log.e(TAG, "Error loading messages", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper function to send a message to the chat
|
||||||
|
private void sendMessage() {
|
||||||
|
//check if a chat is selected
|
||||||
|
if (activeConversationId == null) return;
|
||||||
|
|
||||||
|
//get the message from text field
|
||||||
|
String text = etMessage.getText().toString().trim();
|
||||||
|
if (text.isEmpty()) return;
|
||||||
|
|
||||||
|
//clear text field after sending
|
||||||
|
etMessage.setText("");
|
||||||
|
|
||||||
|
//calls api to send the message
|
||||||
|
messageApi.sendMessage(activeConversationId, new SendMessageRequest(text))
|
||||||
|
.enqueue(new Callback<MessageDTO>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<MessageDTO> call,
|
||||||
|
@NonNull Response<MessageDTO> response) {
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
messageList.add(dtoToModel(response.body()));
|
||||||
|
messageAdapter.notifyItemInserted(messageList.size() - 1);
|
||||||
|
scrollToBottom();
|
||||||
|
loadConversations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<MessageDTO> call,
|
||||||
|
@NonNull Throwable t) {
|
||||||
|
Log.e(TAG, "Send failed", t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a message is received updates the chat preview
|
||||||
|
@Override
|
||||||
|
public void onMessageReceived(MessageDTO dto) {
|
||||||
|
//if there is no active selected conversation or the message received is for another chat, then just update the preview of last message
|
||||||
|
if (activeConversationId == null || !activeConversationId.equals(dto.getConversationId())) {
|
||||||
|
updateConversationPreview(dto.getConversationId(), dto.getContent());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateConversationPreview(dto.getConversationId(), dto.getContent());
|
||||||
|
|
||||||
|
if (currentUserId != null && currentUserId.equals(dto.getSenderId())) return;
|
||||||
|
|
||||||
|
//else add the message to the active chat if it's not from the current user
|
||||||
|
messageList.add(dtoToModel(dto));
|
||||||
|
messageAdapter.notifyItemInserted(messageList.size() - 1);
|
||||||
|
scrollToBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a new conversation is added, updates the chat preview
|
||||||
|
@Override
|
||||||
|
public void onConversationUpdated(ConversationDTO dto) {
|
||||||
|
boolean updated = false;
|
||||||
|
String name = customerNames.getOrDefault(
|
||||||
|
dto.getCustomerId(), "Customer #" + dto.getCustomerId());
|
||||||
|
|
||||||
|
for (int i = 0; i < chatList.size(); i++) {
|
||||||
|
Chat existing = chatList.get(i);
|
||||||
|
if (existing.getChatId().equals(String.valueOf(dto.getId()))) {
|
||||||
|
chatList.set(i, new Chat(
|
||||||
|
String.valueOf(dto.getId()),
|
||||||
|
name,
|
||||||
|
dto.getLastMessage(),
|
||||||
|
dto.getCustomerId(),
|
||||||
|
dto.getStaffId()
|
||||||
|
));
|
||||||
|
chatAdapter.notifyItemChanged(i);
|
||||||
|
updated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!updated) {
|
||||||
|
chatList.add(0, new Chat(
|
||||||
|
String.valueOf(dto.getId()),
|
||||||
|
name,
|
||||||
|
dto.getLastMessage(),
|
||||||
|
dto.getCustomerId(),
|
||||||
|
dto.getStaffId()
|
||||||
|
));
|
||||||
|
chatAdapter.notifyItemInserted(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeConversationId != null && activeConversationId.equals(dto.getId())) {
|
||||||
|
setConversationActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSocketOpened() {
|
||||||
|
if (!isAdded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadConversations();
|
||||||
|
if (activeConversationId != null) {
|
||||||
|
loadMessageHistory(activeConversationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSocketClosed() {
|
||||||
|
if (!isAdded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadConversations();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSocketError() {
|
||||||
|
if (!isAdded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
loadConversations();
|
||||||
|
if (activeConversationId != null) {
|
||||||
|
loadMessageHistory(activeConversationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to convert DTO to message
|
||||||
|
private Message dtoToModel(MessageDTO dto) {
|
||||||
|
Message m = new Message();
|
||||||
|
m.setId(dto.getId());
|
||||||
|
m.setConversationId(dto.getConversationId());
|
||||||
|
m.setSenderId(dto.getSenderId());
|
||||||
|
m.setContent(dto.getContent());
|
||||||
|
m.setTimestamp(dto.getTimestamp());
|
||||||
|
m.setIsRead(dto.getIsRead());
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper function to scroll to bottom of the chat
|
||||||
|
private void scrollToBottom() {
|
||||||
|
if (!messageList.isEmpty()) {
|
||||||
|
rvMessages.post(() ->
|
||||||
|
rvMessages.smoothScrollToPosition(messageList.size() - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to update the chat preview last message
|
||||||
|
private void updateConversationPreview(Long conversationId, String lastMessage) {
|
||||||
|
if (conversationId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < chatList.size(); i++) {
|
||||||
|
Chat existing = chatList.get(i);
|
||||||
|
if (existing.getChatId().equals(String.valueOf(conversationId))) {
|
||||||
|
Chat updated = new Chat(
|
||||||
|
existing.getChatId(),
|
||||||
|
existing.getCustomerName(),
|
||||||
|
lastMessage,
|
||||||
|
existing.getCustomerId(),
|
||||||
|
existing.getStaffId()
|
||||||
|
);
|
||||||
|
chatList.set(i, updated);
|
||||||
|
chatAdapter.notifyItemChanged(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper function to enable or disable the send button when there is no active chat
|
||||||
|
private void setConversationActive(boolean active) {
|
||||||
|
btnSend.setEnabled(active);
|
||||||
|
etMessage.setEnabled(active);
|
||||||
|
if (!active) {
|
||||||
|
activeConversationId = null;
|
||||||
|
if (stompChatManager != null) {
|
||||||
|
stompChatManager.clearConversationSubscription();
|
||||||
|
}
|
||||||
|
messageList.clear();
|
||||||
|
messageAdapter.notifyDataSetChanged();
|
||||||
|
etMessage.setText("");
|
||||||
|
etMessage.setHint("Select a chat to start messaging");
|
||||||
|
} else {
|
||||||
|
etMessage.setHint("Type a message...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When fragment is destroyed, disconnect from websocket
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
if (stompChatManager != null) stompChatManager.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
package com.example.petstoremobile.fragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.core.view.GravityCompat;
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.PetFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.ServiceFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.SupplierFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.AdoptionFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.AppointmentFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.InventoryFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.ProductFragment;
|
||||||
|
|
||||||
|
//The Fragment for the displaying the list of entities to be viewed
|
||||||
|
public class ListFragment extends Fragment {
|
||||||
|
|
||||||
|
private DrawerLayout drawerLayout;
|
||||||
|
private LinearLayout drawerPets, drawerServices, drawerSuppliers;
|
||||||
|
private View touchBlocker;
|
||||||
|
|
||||||
|
// Adoptions, Appointments, Inventory, Products
|
||||||
|
|
||||||
|
private LinearLayout drawerAdoptions, drawerAppointments, drawerInventory, drawerProducts;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_list, container, false);
|
||||||
|
|
||||||
|
//get controls from the layout
|
||||||
|
drawerLayout = view.findViewById(R.id.drawerLayout);
|
||||||
|
drawerPets = view.findViewById(R.id.drawerPets);
|
||||||
|
drawerServices = view.findViewById(R.id.drawerServices);
|
||||||
|
drawerSuppliers = view.findViewById(R.id.drawerSuppliers);
|
||||||
|
drawerAdoptions = view.findViewById(R.id.drawerAdoptions);
|
||||||
|
drawerAppointments = view.findViewById(R.id.drawerAppointments);
|
||||||
|
drawerInventory = view.findViewById(R.id.drawerInventory);
|
||||||
|
drawerProducts = view.findViewById(R.id.drawerProducts);
|
||||||
|
|
||||||
|
//needed to disable touches on the innerContainer while the drawer is open
|
||||||
|
touchBlocker = view.findViewById(R.id.touchBlocker);
|
||||||
|
|
||||||
|
//Display pets fragment by default
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
loadFragment(new PetFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
//add Listeners to the drawer so user won't be able to interact with the innerContainer (the list fragments)
|
||||||
|
//while the drawer is open
|
||||||
|
drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
|
||||||
|
|
||||||
|
//When the drawer is opened, disable touches on the background
|
||||||
|
@Override
|
||||||
|
public void onDrawerOpened(View drawerView) {
|
||||||
|
touchBlocker.setVisibility(View.VISIBLE);
|
||||||
|
touchBlocker.setClickable(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//When the drawer is closed, enable touches again
|
||||||
|
@Override
|
||||||
|
public void onDrawerClosed(View drawerView) {
|
||||||
|
touchBlocker.setVisibility(View.GONE);
|
||||||
|
touchBlocker.setClickable(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//unused methods
|
||||||
|
@Override
|
||||||
|
public void onDrawerSlide(View drawerView, float slideOffset) {}
|
||||||
|
@Override
|
||||||
|
public void onDrawerStateChanged(int newState) {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Click listeners for each drawer
|
||||||
|
//Pets
|
||||||
|
drawerPets.setOnClickListener(v -> {
|
||||||
|
loadFragment(new PetFragment());
|
||||||
|
drawerLayout.closeDrawers();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Services
|
||||||
|
drawerServices.setOnClickListener(v -> {
|
||||||
|
loadFragment(new ServiceFragment());
|
||||||
|
drawerLayout.closeDrawers();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Suppliers
|
||||||
|
drawerSuppliers.setOnClickListener(v -> {
|
||||||
|
loadFragment(new SupplierFragment());
|
||||||
|
drawerLayout.closeDrawers();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Adoptions
|
||||||
|
|
||||||
|
drawerAdoptions.setOnClickListener(v -> {
|
||||||
|
loadFragment(new AdoptionFragment());
|
||||||
|
drawerLayout.closeDrawers();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Appoinment
|
||||||
|
drawerAppointments.setOnClickListener(v -> {
|
||||||
|
loadFragment(new AppointmentFragment());
|
||||||
|
drawerLayout.closeDrawers();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Inventory
|
||||||
|
drawerInventory.setOnClickListener(v -> {
|
||||||
|
loadFragment(new InventoryFragment());
|
||||||
|
drawerLayout.closeDrawers();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Products
|
||||||
|
drawerProducts.setOnClickListener(v -> {
|
||||||
|
loadFragment(new ProductFragment());
|
||||||
|
drawerLayout.closeDrawers();
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to open the drawer
|
||||||
|
public void openDrawer() {
|
||||||
|
drawerLayout.openDrawer(GravityCompat.START);
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function to load the fragment into the display
|
||||||
|
public void loadFragment(Fragment fragment) {
|
||||||
|
getChildFragmentManager()
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(R.id.inner_fragment_container, fragment)
|
||||||
|
.addToBackStack(null)
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
package com.example.petstoremobile.fragments;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.activities.MainActivity;
|
||||||
|
import com.example.petstoremobile.api.auth.TokenManager;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class ProfileFragment extends Fragment {
|
||||||
|
|
||||||
|
//initialize the view/controls
|
||||||
|
private ImageView imgProfile;
|
||||||
|
private TextView tvProfileName, tvProfileEmail, tvProfilePhone, tvProfileRole;
|
||||||
|
private Button btnChangePhoto, btnEditEmail, btnEditPhone, btnLogout;
|
||||||
|
private Uri photoUri;
|
||||||
|
|
||||||
|
//Initialize the launchers for camera and gallery
|
||||||
|
private ActivityResultLauncher<Intent> galleryLauncher;
|
||||||
|
private ActivityResultLauncher<Uri> cameraLauncher;
|
||||||
|
private ActivityResultLauncher<String> permissionLauncher;
|
||||||
|
|
||||||
|
//Called when the fragment is created, sets up the launchers is set profile image
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Launcher to open gallery to select profile image
|
||||||
|
galleryLauncher = registerForActivityResult(
|
||||||
|
//open gallery
|
||||||
|
new ActivityResultContracts.StartActivityForResult(),
|
||||||
|
result -> {
|
||||||
|
//if the user selects an image and its not null
|
||||||
|
if (result.getResultCode() == Activity.RESULT_OK
|
||||||
|
&& result.getData() != null) {
|
||||||
|
//get the selected image and set the image to the profile
|
||||||
|
Uri selectedImage = result.getData().getData();
|
||||||
|
imgProfile.setImageURI(selectedImage);
|
||||||
|
//TODO: SAVE CHANGED PHOTO TO DATABASE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Launcher for camera to open and capture profile image
|
||||||
|
cameraLauncher = registerForActivityResult(
|
||||||
|
//open camera
|
||||||
|
new ActivityResultContracts.TakePicture(),
|
||||||
|
success -> {
|
||||||
|
//if a photo is taken set the image profile to it otherwise do nothing
|
||||||
|
if (success) {
|
||||||
|
//Clear the old image and set the new one
|
||||||
|
imgProfile.setImageURI(null);
|
||||||
|
imgProfile.setImageURI(photoUri);
|
||||||
|
//TODO: SAVE CHANGED PHOTO TO DATABASE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Launcher to request camera permission
|
||||||
|
permissionLauncher = registerForActivityResult(
|
||||||
|
//ask user for camera permission
|
||||||
|
new ActivityResultContracts.RequestPermission(),
|
||||||
|
granted -> {
|
||||||
|
//if the permission is granted launch the camera
|
||||||
|
if (granted) {
|
||||||
|
launchCamera();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//if the permission is denied then tell the user to grant it
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Permission Permission Required")
|
||||||
|
.setMessage("Please grant camera permission to use this feature")
|
||||||
|
.setPositiveButton("Open Settings", (dialog, which) ->{
|
||||||
|
//open the settings page to grant the permission when they click open settings
|
||||||
|
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||||
|
intent.setData(Uri.fromParts("package", requireContext().getPackageName(), null));
|
||||||
|
startActivity(intent);
|
||||||
|
})
|
||||||
|
//close the dialog when the user clicks cancel
|
||||||
|
.setNegativeButton("Cancel", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: MAKE PROFILE VIEW DISPLAY PROFILE DATA FROM DATABASE
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_profile, container, false);
|
||||||
|
|
||||||
|
//get all the controls from the view
|
||||||
|
imgProfile = view.findViewById(R.id.imgProfile);
|
||||||
|
tvProfileName = view.findViewById(R.id.tvProfileName);
|
||||||
|
tvProfileEmail = view.findViewById(R.id.tvProfileEmail);
|
||||||
|
tvProfilePhone = view.findViewById(R.id.tvProfilePhone);
|
||||||
|
tvProfileRole = view.findViewById(R.id.tvProfileRole);
|
||||||
|
btnChangePhoto = view.findViewById(R.id.btnChangePhoto);
|
||||||
|
btnEditEmail = view.findViewById(R.id.btnEditEmail);
|
||||||
|
btnEditPhone = view.findViewById(R.id.btnEditPhone);
|
||||||
|
btnLogout = view.findViewById(R.id.btnLogout);
|
||||||
|
|
||||||
|
//Set up listeners for the buttons
|
||||||
|
//Change photo button
|
||||||
|
btnChangePhoto.setOnClickListener(v -> {
|
||||||
|
//Show alert dialog to user to select from gallery or camera
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Change Profile Photo")
|
||||||
|
//set the options for the alert dialog
|
||||||
|
.setItems(new String[]{"Take Photo", "Choose from Gallery"}, (dialog, which) -> {
|
||||||
|
if (which == 0) {
|
||||||
|
// Choose Camera
|
||||||
|
//Checks if the user has granted the camera permission already
|
||||||
|
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
//if the permission is already granted then launch the camera
|
||||||
|
launchCamera();
|
||||||
|
} else {
|
||||||
|
//otherwise request the permission
|
||||||
|
permissionLauncher.launch(Manifest.permission.CAMERA);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Choose Gallery
|
||||||
|
Intent intent = new Intent(Intent.ACTION_PICK,
|
||||||
|
MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||||
|
galleryLauncher.launch(intent);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
//TODO: UPDATE PHOTO IN DATABASE
|
||||||
|
});
|
||||||
|
|
||||||
|
//Edit email button
|
||||||
|
//When clicked open a dialog to change email
|
||||||
|
btnEditEmail.setOnClickListener(v -> {
|
||||||
|
//Make a text field for the user to enter the new email
|
||||||
|
EditText input = new EditText(requireContext());
|
||||||
|
input.setPadding(30,30,30,30);
|
||||||
|
input.setText(tvProfileEmail.getText().toString());
|
||||||
|
|
||||||
|
//set input type to email
|
||||||
|
input.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
|
||||||
|
|
||||||
|
|
||||||
|
//Show alert dialog to user to enter new email
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Edit Email")
|
||||||
|
.setView(input)
|
||||||
|
.setPositiveButton("Save", (dialog, which) -> {
|
||||||
|
String newEmail = input.getText().toString();
|
||||||
|
//if the new value is a valid email then set the email to the new value
|
||||||
|
if (android.util.Patterns.EMAIL_ADDRESS.matcher(newEmail).matches()) {
|
||||||
|
tvProfileEmail.setText(newEmail);
|
||||||
|
//TODO: UPDATE THE EMAIL IN DATABASE
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//tell the user to email is invalid
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Error")
|
||||||
|
.setMessage("Email is invalid")
|
||||||
|
.setPositiveButton("OK", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancel", null)
|
||||||
|
.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Edit phone button
|
||||||
|
//When clicked open a dialog to change phone
|
||||||
|
btnEditPhone.setOnClickListener(v -> {
|
||||||
|
//Make a text field for the user to enter the new email
|
||||||
|
EditText input = new EditText(requireContext());
|
||||||
|
input.setPadding(30,30,30,30);
|
||||||
|
input.setText(tvProfilePhone.getText().toString());
|
||||||
|
|
||||||
|
//set input type to phone number
|
||||||
|
input.setInputType(InputType.TYPE_CLASS_PHONE);
|
||||||
|
|
||||||
|
//add canada phone number formatting to input (XXX) XXX-XXXX
|
||||||
|
input.addTextChangedListener(new android.telephony.PhoneNumberFormattingTextWatcher("CA"));
|
||||||
|
input.setFilters(new android.text.InputFilter[]{new android.text.InputFilter.LengthFilter(14)});
|
||||||
|
|
||||||
|
|
||||||
|
//Show alert dialog to user to enter new phone
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Edit Phone Number")
|
||||||
|
.setView(input)
|
||||||
|
.setPositiveButton("Save", (dialog, which) -> {
|
||||||
|
String newPhone = input.getText().toString();
|
||||||
|
//if the new value is format: (XXX) XXX-XXXX then set the phone to the new value
|
||||||
|
if (newPhone.matches("\\(\\d{3}\\) \\d{3}-\\d{4}")) { //TODO MAKE VALIDATION CLASS INSTEAD FOR THIS
|
||||||
|
tvProfilePhone.setText(newPhone);
|
||||||
|
//TODO: UPDATE PHONE IN DATABASE
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//tell the user to email cannot be empty
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Error")
|
||||||
|
.setMessage("Phone number is invalid. Format: (XXX) XXX-XXXX")
|
||||||
|
.setPositiveButton("OK", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancel", null)
|
||||||
|
.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
//Logout button
|
||||||
|
btnLogout.setOnClickListener(v -> {
|
||||||
|
TokenManager.getInstance(requireContext()).clearLoginData(); // clear the token for next login
|
||||||
|
//get the intent to the main activity and clear the back stack so the back button won't allow the user to go back to the previous screen
|
||||||
|
Intent intent = new Intent(getActivity(), MainActivity.class);
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
//start the activity to go to login page and finish the current activity
|
||||||
|
startActivity(intent);
|
||||||
|
requireActivity().finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper function create a file in the cache directory to store the photo in then launch the camera to capture the photo
|
||||||
|
private void launchCamera() {
|
||||||
|
//create a file in the cache directory to store the photo in
|
||||||
|
File photoFile = new File(requireContext().getCacheDir(), "profile_photo.jpg");
|
||||||
|
//get the uri for the file made
|
||||||
|
photoUri = FileProvider.getUriForFile(requireContext(), requireContext().getPackageName() + ".fileprovider", photoFile);
|
||||||
|
//launch the camera to capture the photo and save the photo to photoUri
|
||||||
|
cameraLauncher.launch(photoUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments;
|
||||||
|
|
||||||
|
// Added search/filter bar to filter adoptions by adopter name or pet name.
|
||||||
|
// Added pull-to-refresh using SwipeRefreshLayout.
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.adapters.AdoptionAdapter;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.detailfragments.AdoptionDetailFragment;
|
||||||
|
import com.example.petstoremobile.models.Adoption;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AdoptionFragment extends Fragment implements AdoptionAdapter.OnAdoptionClickListener {
|
||||||
|
|
||||||
|
private List<Adoption> adoptionList = new ArrayList<>();
|
||||||
|
private List<Adoption> filteredList = new ArrayList<>();
|
||||||
|
private AdoptionAdapter adapter;
|
||||||
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
|
private EditText etSearch;
|
||||||
|
private ImageButton hamburger;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_adoption, container, false);
|
||||||
|
|
||||||
|
hamburger = view.findViewById(R.id.btnHamburger);
|
||||||
|
|
||||||
|
loadAdoptionData();
|
||||||
|
// Replace with actual API call when backend is ready
|
||||||
|
setupRecyclerView(view);
|
||||||
|
setupSearch(view);
|
||||||
|
setupSwipeRefresh(view);
|
||||||
|
|
||||||
|
FloatingActionButton fabAddAdoption = view.findViewById(R.id.fabAddAdoption);
|
||||||
|
fabAddAdoption.setOnClickListener(v -> openAdoptionDetails(-1));
|
||||||
|
|
||||||
|
//Make the hamburger button open the drawer from listFragment
|
||||||
|
hamburger.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
//if list fragment is found then use its helper function to open the drawer
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.openDrawer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filters adoption list by adopter name or pet name
|
||||||
|
private void setupSearch(View view) {
|
||||||
|
etSearch = view.findViewById(R.id.etSearchAdoption);
|
||||||
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
filterAdoptions(s.toString());
|
||||||
|
}
|
||||||
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterAdoptions(String query) {
|
||||||
|
filteredList.clear();
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
filteredList.addAll(adoptionList);
|
||||||
|
} else {
|
||||||
|
String lower = query.toLowerCase();
|
||||||
|
for (Adoption a : adoptionList) {
|
||||||
|
if (a.getAdopterName().toLowerCase().contains(lower)
|
||||||
|
|| a.getPetName().toLowerCase().contains(lower)
|
||||||
|
|| a.getStatus().toLowerCase().contains(lower)) {
|
||||||
|
filteredList.add(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSwipeRefresh(View view) {
|
||||||
|
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshAdoption);
|
||||||
|
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
|
loadAdoptionData(); // TODO: Replace with actual API call
|
||||||
|
filterAdoptions(etSearch.getText().toString());
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openAdoptionDetails(int position) {
|
||||||
|
AdoptionDetailFragment detailFragment = new AdoptionDetailFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("position", position);
|
||||||
|
|
||||||
|
if (position != -1) {
|
||||||
|
Adoption adoption = filteredList.get(position);
|
||||||
|
int realPosition = adoptionList.indexOf(adoption);
|
||||||
|
args.putInt("position", realPosition);
|
||||||
|
args.putInt("adoptionId", adoption.getAdoptionId());
|
||||||
|
args.putString("adopterName", adoption.getAdopterName());
|
||||||
|
args.putString("adopterEmail", adoption.getAdopterEmail());
|
||||||
|
args.putString("adopterPhone", adoption.getAdopterPhone());
|
||||||
|
args.putString("petName", adoption.getPetName());
|
||||||
|
args.putString("adoptionDate", adoption.getAdoptionDate());
|
||||||
|
args.putString("status", adoption.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
detailFragment.setArguments(args);
|
||||||
|
detailFragment.setAdoptionFragment(this);
|
||||||
|
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.loadFragment(detailFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAdoptionSaved(int position, Adoption adoption) {
|
||||||
|
if (position == -1) {
|
||||||
|
adoptionList.add(adoption);
|
||||||
|
} else {
|
||||||
|
adoptionList.set(position, adoption);
|
||||||
|
}
|
||||||
|
filterAdoptions(etSearch.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAdoptionDeleted(int position) {
|
||||||
|
adoptionList.remove(position);
|
||||||
|
filterAdoptions(etSearch.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAdoptionClick(int position) {
|
||||||
|
openAdoptionDetails(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadAdoptionData() {
|
||||||
|
adoptionList.clear();
|
||||||
|
adoptionList.add(new Adoption(1, "Sarah Connor", "sarah@email.com", "555-1234", "Luna", "2026-03-01", "Approved"));
|
||||||
|
adoptionList.add(new Adoption(2, "Tom Hardy", "tom@email.com", "555-5678", "Bella", "2026-03-05", "Pending"));
|
||||||
|
adoptionList.add(new Adoption(3, "Emily Clark", "emily@email.com", "555-9012", "Charlie", "2026-03-07", "Pending"));
|
||||||
|
adoptionList.add(new Adoption(4, "Mike Ross", "mike@email.com", "555-3456", "Milo", "2026-02-20", "Rejected"));
|
||||||
|
filteredList.clear();
|
||||||
|
filteredList.addAll(adoptionList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRecyclerView(View view) {
|
||||||
|
RecyclerView recyclerView = view.findViewById(R.id.recyclerViewAdoptions);
|
||||||
|
adapter = new AdoptionAdapter(filteredList, this);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments;
|
||||||
|
|
||||||
|
// Added search/filter bar to filter appointments by customer name or service type.
|
||||||
|
// Added pull-to-refresh using SwipeRefreshLayout.
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.adapters.AppointmentAdapter;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.detailfragments.AppointmentDetailFragment;
|
||||||
|
import com.example.petstoremobile.models.Appointment;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AppointmentFragment extends Fragment implements AppointmentAdapter.OnAppointmentClickListener {
|
||||||
|
|
||||||
|
private List<Appointment> appointmentList = new ArrayList<>(); // full data list
|
||||||
|
private List<Appointment> filteredList = new ArrayList<>(); // filtered display list
|
||||||
|
private AppointmentAdapter adapter;
|
||||||
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
|
private EditText etSearch;
|
||||||
|
private ImageButton hamburger;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_appointment, container, false);
|
||||||
|
|
||||||
|
hamburger = view.findViewById(R.id.btnHamburger);
|
||||||
|
|
||||||
|
loadAppointmentData(); // TODO: Replace with actual API call when backend is ready
|
||||||
|
setupRecyclerView(view);
|
||||||
|
setupSearch(view);
|
||||||
|
setupSwipeRefresh(view);
|
||||||
|
|
||||||
|
FloatingActionButton fabAddAppointment = view.findViewById(R.id.fabAddAppointment);
|
||||||
|
fabAddAppointment.setOnClickListener(v -> openAppointmentDetails(-1));
|
||||||
|
|
||||||
|
//Make the hamburger button open the drawer from listFragment
|
||||||
|
hamburger.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
//if list fragment is found then use its helper function to open the drawer
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.openDrawer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets up the search bar to filter appointments by customer name or service type
|
||||||
|
private void setupSearch(View view) {
|
||||||
|
etSearch = view.findViewById(R.id.etSearchAppointment);
|
||||||
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
filterAppointments(s.toString());
|
||||||
|
}
|
||||||
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filters the appointment list based on the search query
|
||||||
|
private void filterAppointments(String query) {
|
||||||
|
filteredList.clear();
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
filteredList.addAll(appointmentList);
|
||||||
|
} else {
|
||||||
|
String lower = query.toLowerCase();
|
||||||
|
for (Appointment a : appointmentList) {
|
||||||
|
if (a.getCustomerName().toLowerCase().contains(lower)
|
||||||
|
|| a.getServiceType().toLowerCase().contains(lower)
|
||||||
|
|| a.getPetName().toLowerCase().contains(lower)) {
|
||||||
|
filteredList.add(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets up pull-to-refresh: reloads data when user swipes down
|
||||||
|
private void setupSwipeRefresh(View view) {
|
||||||
|
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshAppointment);
|
||||||
|
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
|
loadAppointmentData(); // TODO: Replace with actual API call when backend is ready
|
||||||
|
filterAppointments(etSearch.getText().toString());
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openAppointmentDetails(int position) {
|
||||||
|
AppointmentDetailFragment detailFragment = new AppointmentDetailFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("position", position);
|
||||||
|
|
||||||
|
if (position != -1) {
|
||||||
|
Appointment appointment = filteredList.get(position);
|
||||||
|
// Find the real position in the full list for save/delete callbacks
|
||||||
|
int realPosition = appointmentList.indexOf(appointment);
|
||||||
|
args.putInt("position", realPosition);
|
||||||
|
args.putInt("appointmentId", appointment.getAppointmentId());
|
||||||
|
args.putString("customerName", appointment.getCustomerName());
|
||||||
|
args.putString("petName", appointment.getPetName());
|
||||||
|
args.putString("serviceType", appointment.getServiceType());
|
||||||
|
args.putString("appointmentDate", appointment.getAppointmentDate());
|
||||||
|
args.putString("appointmentTime", appointment.getAppointmentTime());
|
||||||
|
args.putString("status", appointment.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
detailFragment.setArguments(args);
|
||||||
|
detailFragment.setAppointmentFragment(this);
|
||||||
|
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.loadFragment(detailFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAppointmentSaved(int position, Appointment appointment) {
|
||||||
|
if (position == -1) {
|
||||||
|
appointmentList.add(appointment);
|
||||||
|
} else {
|
||||||
|
appointmentList.set(position, appointment);
|
||||||
|
}
|
||||||
|
filterAppointments(etSearch.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAppointmentDeleted(int position) {
|
||||||
|
appointmentList.remove(position);
|
||||||
|
filterAppointments(etSearch.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAppointmentClick(int position) {
|
||||||
|
openAppointmentDetails(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to load hardcoded sample data
|
||||||
|
// Replace with API call
|
||||||
|
private void loadAppointmentData() {
|
||||||
|
appointmentList.clear();
|
||||||
|
appointmentList.add(new Appointment(1, "John Smith", "Buddy", "Grooming", "2026-03-10", "10:00 AM", "Confirmed"));
|
||||||
|
appointmentList.add(new Appointment(2, "Jane Doe", "Luna", "Vet Checkup", "2026-03-11", "02:00 PM", "Pending"));
|
||||||
|
appointmentList.add(new Appointment(3, "Bob Lee", "Max", "Training", "2026-03-12", "11:00 AM", "Confirmed"));
|
||||||
|
appointmentList.add(new Appointment(4, "Alice Brown", "Milo", "Grooming", "2026-03-13", "03:00 PM", "Cancelled"));
|
||||||
|
filteredList.clear();
|
||||||
|
filteredList.addAll(appointmentList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRecyclerView(View view) {
|
||||||
|
RecyclerView recyclerView = view.findViewById(R.id.recyclerViewAppointments);
|
||||||
|
adapter = new AppointmentAdapter(filteredList, this); // adapter uses filteredList
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments;
|
||||||
|
|
||||||
|
// Added search/filter bar to filter inventory by item name or category.
|
||||||
|
// Added pull-to-refresh using SwipeRefreshLayout.
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.adapters.InventoryAdapter;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.detailfragments.InventoryDetailFragment;
|
||||||
|
import com.example.petstoremobile.models.Inventory;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class InventoryFragment extends Fragment implements InventoryAdapter.OnInventoryClickListener {
|
||||||
|
|
||||||
|
private List<Inventory> inventoryList = new ArrayList<>();
|
||||||
|
private List<Inventory> filteredList = new ArrayList<>();
|
||||||
|
private InventoryAdapter adapter;
|
||||||
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
|
private EditText etSearch;
|
||||||
|
private ImageButton hamburger;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_inventory, container, false);
|
||||||
|
|
||||||
|
hamburger = view.findViewById(R.id.btnHamburger);
|
||||||
|
|
||||||
|
loadInventoryData(); // TODO: Replace with actual API call when backend is ready
|
||||||
|
setupRecyclerView(view);
|
||||||
|
setupSearch(view);
|
||||||
|
setupSwipeRefresh(view);
|
||||||
|
|
||||||
|
FloatingActionButton fabAddInventory = view.findViewById(R.id.fabAddInventory);
|
||||||
|
fabAddInventory.setOnClickListener(v -> openInventoryDetails(-1));
|
||||||
|
|
||||||
|
//Make the hamburger button open the drawer from listFragment
|
||||||
|
hamburger.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
//if list fragment is found then use its helper function to open the drawer
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.openDrawer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filters inventory list by item name or category
|
||||||
|
private void setupSearch(View view) {
|
||||||
|
etSearch = view.findViewById(R.id.etSearchInventory);
|
||||||
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
filterInventory(s.toString());
|
||||||
|
}
|
||||||
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterInventory(String query) {
|
||||||
|
filteredList.clear();
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
filteredList.addAll(inventoryList);
|
||||||
|
} else {
|
||||||
|
String lower = query.toLowerCase();
|
||||||
|
for (Inventory i : inventoryList) {
|
||||||
|
if (i.getItemName().toLowerCase().contains(lower)
|
||||||
|
|| i.getCategory().toLowerCase().contains(lower)
|
||||||
|
|| i.getSupplier().toLowerCase().contains(lower)) {
|
||||||
|
filteredList.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSwipeRefresh(View view) {
|
||||||
|
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshInventory);
|
||||||
|
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
|
loadInventoryData(); // TODO: Replace with actual API call
|
||||||
|
filterInventory(etSearch.getText().toString());
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openInventoryDetails(int position) {
|
||||||
|
InventoryDetailFragment detailFragment = new InventoryDetailFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("position", position);
|
||||||
|
|
||||||
|
if (position != -1) {
|
||||||
|
Inventory inventory = filteredList.get(position);
|
||||||
|
int realPosition = inventoryList.indexOf(inventory);
|
||||||
|
args.putInt("position", realPosition);
|
||||||
|
args.putInt("inventoryId", inventory.getInventoryId());
|
||||||
|
args.putString("itemName", inventory.getItemName());
|
||||||
|
args.putString("category", inventory.getCategory());
|
||||||
|
args.putInt("quantity", inventory.getQuantity());
|
||||||
|
args.putDouble("unitPrice", inventory.getUnitPrice());
|
||||||
|
args.putString("supplier", inventory.getSupplier());
|
||||||
|
}
|
||||||
|
|
||||||
|
detailFragment.setArguments(args);
|
||||||
|
detailFragment.setInventoryFragment(this);
|
||||||
|
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.loadFragment(detailFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onInventorySaved(int position, Inventory inventory) {
|
||||||
|
if (position == -1) {
|
||||||
|
inventoryList.add(inventory);
|
||||||
|
} else {
|
||||||
|
inventoryList.set(position, inventory);
|
||||||
|
}
|
||||||
|
filterInventory(etSearch.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onInventoryDeleted(int position) {
|
||||||
|
inventoryList.remove(position);
|
||||||
|
filterInventory(etSearch.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInventoryClick(int position) {
|
||||||
|
openInventoryDetails(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadInventoryData() {
|
||||||
|
inventoryList.clear();
|
||||||
|
inventoryList.add(new Inventory(1, "Dog Food - Large", "Food", 50, 25.99, "PetSupplies Co."));
|
||||||
|
inventoryList.add(new Inventory(2, "Cat Litter", "Hygiene", 30, 12.99, "CleanPaws Ltd."));
|
||||||
|
inventoryList.add(new Inventory(3, "Dog Leash", "Accessories", 4, 15.99, "PetGear Inc."));
|
||||||
|
inventoryList.add(new Inventory(4, "Bird Cage - Medium", "Housing", 8, 79.99, "BirdWorld"));
|
||||||
|
inventoryList.add(new Inventory(5, "Flea Treatment", "Medicine", 2, 34.99, "VetCare Supply"));
|
||||||
|
filteredList.clear();
|
||||||
|
filteredList.addAll(inventoryList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRecyclerView(View view) {
|
||||||
|
RecyclerView recyclerView = view.findViewById(R.id.recyclerViewInventory);
|
||||||
|
adapter = new InventoryAdapter(filteredList, this);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.adapters.PetAdapter;
|
||||||
|
import com.example.petstoremobile.api.PetApi;
|
||||||
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
|
import com.example.petstoremobile.dtos.PageResponse;
|
||||||
|
import com.example.petstoremobile.dtos.PetDTO;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.detailfragments.PetDetailFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.listprofilefragments.PetProfileFragment;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class PetFragment extends Fragment implements PetAdapter.OnPetClickListener {
|
||||||
|
private List<PetDTO> petList = new ArrayList<>();
|
||||||
|
private List<PetDTO> filteredList = new ArrayList<>();
|
||||||
|
private ImageButton hamburger;
|
||||||
|
private PetAdapter adapter;
|
||||||
|
private PetApi api;
|
||||||
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
|
private EditText etSearch;
|
||||||
|
|
||||||
|
//load pet view
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_pet, container, false);
|
||||||
|
|
||||||
|
//get retrofit
|
||||||
|
api = RetrofitClient.getPetApi(requireContext());
|
||||||
|
|
||||||
|
hamburger = view.findViewById(R.id.btnHamburger);
|
||||||
|
|
||||||
|
setupRecyclerView(view);
|
||||||
|
setupSearch(view);
|
||||||
|
setupSwipeRefresh(view);
|
||||||
|
loadPetData();
|
||||||
|
|
||||||
|
|
||||||
|
//Add button to opens the add dialog
|
||||||
|
FloatingActionButton fabAddPet = view.findViewById(R.id.fabAddPet);
|
||||||
|
fabAddPet.setOnClickListener(v -> openPetDetails(-1));
|
||||||
|
|
||||||
|
//Make the hamburger button open the drawer from listFragment
|
||||||
|
hamburger.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
//if list fragment is found then use its helper function to open the drawer
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.openDrawer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSearch(View view) {
|
||||||
|
etSearch = view.findViewById(R.id.etSearchPet);
|
||||||
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
filterPets(s.toString());
|
||||||
|
}
|
||||||
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterPets(String query) {
|
||||||
|
filteredList.clear();
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
filteredList.addAll(petList);
|
||||||
|
} else {
|
||||||
|
String lower = query.toLowerCase();
|
||||||
|
for (PetDTO p : petList) {
|
||||||
|
if (p.getPetName().toLowerCase().contains(lower)
|
||||||
|
|| p.getPetSpecies().toLowerCase().contains(lower)
|
||||||
|
|| p.getPetBreed().toLowerCase().contains(lower)) {
|
||||||
|
filteredList.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSwipeRefresh(View view) {
|
||||||
|
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshPet);
|
||||||
|
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
|
loadPetData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Open pet profile
|
||||||
|
private void openPetProfile(int position) {
|
||||||
|
PetProfileFragment profileFragment = new PetProfileFragment();
|
||||||
|
|
||||||
|
//Make a bundle to pass data to the profile fragment
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
PetDTO pet = filteredList.get(position);
|
||||||
|
args.putInt("petId", pet.getPetId().intValue());
|
||||||
|
args.putString("petName", pet.getPetName());
|
||||||
|
args.putString("petSpecies", pet.getPetSpecies());
|
||||||
|
args.putString("petBreed", pet.getPetBreed());
|
||||||
|
args.putInt("petAge", pet.getPetAge());
|
||||||
|
args.putString("petStatus", pet.getPetStatus());
|
||||||
|
|
||||||
|
try {
|
||||||
|
args.putDouble("petPrice", Double.parseDouble(pet.getPetPrice()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
args.putDouble("petPrice", 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//send the bundle to the profile fragment to display
|
||||||
|
profileFragment.setArguments(args);
|
||||||
|
|
||||||
|
//get ListFragment to load the the pet profile view
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.loadFragment(profileFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Open the pet detail view for adding
|
||||||
|
private void openPetDetails(int position) {
|
||||||
|
PetDetailFragment detailFragment = new PetDetailFragment();
|
||||||
|
|
||||||
|
//get ListFragment to load the detail view
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.loadFragment(detailFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by PetAdapter when a row is clicked to open the details view
|
||||||
|
@Override
|
||||||
|
public void onPetClick(int position) {
|
||||||
|
openPetProfile(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to get a list of all pets from the backend
|
||||||
|
private void loadPetData() {
|
||||||
|
if (swipeRefreshLayout != null) {
|
||||||
|
swipeRefreshLayout.setRefreshing(true);
|
||||||
|
}
|
||||||
|
api.getAllPets(0, 100).enqueue(new Callback<PageResponse<PetDTO>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<PageResponse<PetDTO>> call, Response<PageResponse<PetDTO>> response) {
|
||||||
|
if (swipeRefreshLayout != null) {
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
petList.clear();
|
||||||
|
petList.addAll(response.body().getContent());
|
||||||
|
filterPets(etSearch.getText().toString());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.e("onResponse: ", response.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<PageResponse<PetDTO>> call, Throwable t) {
|
||||||
|
if (swipeRefreshLayout != null) {
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
|
Toast.makeText(getContext(),
|
||||||
|
"Failed to load pets", Toast.LENGTH_SHORT).show();
|
||||||
|
Log.e("onFailure: ", t.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//set up the recyclerview and adapter
|
||||||
|
private void setupRecyclerView(View view) {
|
||||||
|
RecyclerView recyclerView = view.findViewById(R.id.recyclerViewPets);
|
||||||
|
adapter = new PetAdapter(filteredList, this);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments;
|
||||||
|
|
||||||
|
// Added search/filter bar to filter products by name or category.
|
||||||
|
// Added pull-to-refresh using SwipeRefreshLayout.
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.adapters.ProductAdapter;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.detailfragments.ProductDetailFragment;
|
||||||
|
import com.example.petstoremobile.models.Product;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ProductFragment extends Fragment implements ProductAdapter.OnProductClickListener {
|
||||||
|
|
||||||
|
private List<Product> productList = new ArrayList<>();
|
||||||
|
private List<Product> filteredList = new ArrayList<>();
|
||||||
|
private ProductAdapter adapter;
|
||||||
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
|
private EditText etSearch;
|
||||||
|
private ImageButton hamburger;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_product, container, false);
|
||||||
|
|
||||||
|
hamburger = view.findViewById(R.id.btnHamburger);
|
||||||
|
|
||||||
|
loadProductData(); // TODO: Replace with actual API call when backend is ready
|
||||||
|
setupRecyclerView(view);
|
||||||
|
setupSearch(view);
|
||||||
|
setupSwipeRefresh(view);
|
||||||
|
|
||||||
|
FloatingActionButton fabAddProduct = view.findViewById(R.id.fabAddProduct);
|
||||||
|
fabAddProduct.setOnClickListener(v -> openProductDetails(-1));
|
||||||
|
|
||||||
|
//Make the hamburger button open the drawer from listFragment
|
||||||
|
hamburger.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
//if list fragment is found then use its helper function to open the drawer
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.openDrawer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filters products by name, description, or category
|
||||||
|
private void setupSearch(View view) {
|
||||||
|
etSearch = view.findViewById(R.id.etSearchProduct);
|
||||||
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
filterProducts(s.toString());
|
||||||
|
}
|
||||||
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterProducts(String query) {
|
||||||
|
filteredList.clear();
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
filteredList.addAll(productList);
|
||||||
|
} else {
|
||||||
|
String lower = query.toLowerCase();
|
||||||
|
for (Product p : productList) {
|
||||||
|
if (p.getProductName().toLowerCase().contains(lower)
|
||||||
|
|| p.getCategory().toLowerCase().contains(lower)
|
||||||
|
|| p.getProductDesc().toLowerCase().contains(lower)) {
|
||||||
|
filteredList.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSwipeRefresh(View view) {
|
||||||
|
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshProduct);
|
||||||
|
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
|
loadProductData(); // TODO: Replace with actual API call
|
||||||
|
filterProducts(etSearch.getText().toString());
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openProductDetails(int position) {
|
||||||
|
ProductDetailFragment detailFragment = new ProductDetailFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("position", position);
|
||||||
|
|
||||||
|
if (position != -1) {
|
||||||
|
Product product = filteredList.get(position);
|
||||||
|
int realPosition = productList.indexOf(product);
|
||||||
|
args.putInt("position", realPosition);
|
||||||
|
args.putInt("productId", product.getProductId());
|
||||||
|
args.putString("productName", product.getProductName());
|
||||||
|
args.putString("productDesc", product.getProductDesc());
|
||||||
|
args.putString("category", product.getCategory());
|
||||||
|
args.putDouble("productPrice", product.getProductPrice());
|
||||||
|
args.putInt("stockQuantity", product.getStockQuantity());
|
||||||
|
}
|
||||||
|
|
||||||
|
detailFragment.setArguments(args);
|
||||||
|
detailFragment.setProductFragment(this);
|
||||||
|
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.loadFragment(detailFragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onProductSaved(int position, Product product) {
|
||||||
|
if (position == -1) {
|
||||||
|
productList.add(product);
|
||||||
|
} else {
|
||||||
|
productList.set(position, product);
|
||||||
|
}
|
||||||
|
filterProducts(etSearch.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onProductDeleted(int position) {
|
||||||
|
productList.remove(position);
|
||||||
|
filterProducts(etSearch.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProductClick(int position) {
|
||||||
|
openProductDetails(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadProductData() {
|
||||||
|
productList.clear();
|
||||||
|
productList.add(new Product(1, "Premium Dog Food", "High protein dry food for adult dogs", "Food", 45.99, 25));
|
||||||
|
productList.add(new Product(2, "Cat Toy Bundle", "Set of 5 interactive toys", "Toys", 19.99, 40));
|
||||||
|
productList.add(new Product(3, "Pet Shampoo", "Gentle formula for all breeds", "Grooming", 12.99, 60));
|
||||||
|
productList.add(new Product(4, "Dog Bed - Large", "Memory foam orthopedic bed", "Bedding", 89.99, 10));
|
||||||
|
productList.add(new Product(5, "Aquarium Starter Kit", "20-gallon tank with filter and light", "Aquatic", 129.99, 5));
|
||||||
|
filteredList.clear();
|
||||||
|
filteredList.addAll(productList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRecyclerView(View view) {
|
||||||
|
RecyclerView recyclerView = view.findViewById(R.id.recyclerViewProducts);
|
||||||
|
adapter = new ProductAdapter(filteredList, this);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.adapters.ServiceAdapter;
|
||||||
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
|
import com.example.petstoremobile.api.ServiceApi;
|
||||||
|
import com.example.petstoremobile.dtos.PageResponse;
|
||||||
|
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.detailfragments.ServiceDetailFragment;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class ServiceFragment extends Fragment implements ServiceAdapter.OnServiceClickListener {
|
||||||
|
|
||||||
|
private List<ServiceDTO> serviceList = new ArrayList<>();
|
||||||
|
private List<ServiceDTO> filteredList = new ArrayList<>();
|
||||||
|
private ServiceAdapter adapter;
|
||||||
|
private ImageButton hamburger;
|
||||||
|
private ServiceApi api;
|
||||||
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
|
private EditText etSearch;
|
||||||
|
|
||||||
|
//load service view
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_service, container, false);
|
||||||
|
|
||||||
|
api = RetrofitClient.getServiceApi(requireContext());
|
||||||
|
hamburger = view.findViewById(R.id.btnHamburger);
|
||||||
|
|
||||||
|
setupRecyclerView(view);
|
||||||
|
setupSearch(view);
|
||||||
|
setupSwipeRefresh(view);
|
||||||
|
loadServiceData();
|
||||||
|
|
||||||
|
//Add button to opens the add dialog
|
||||||
|
FloatingActionButton fabAddService = view.findViewById(R.id.fabAddService);
|
||||||
|
fabAddService.setOnClickListener(v -> openServiceDetails(-1));
|
||||||
|
|
||||||
|
//Make the hamburger button open the drawer from listFragment
|
||||||
|
hamburger.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
//if list fragment is found then use its helper function to open the drawer
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.openDrawer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSearch(View view) {
|
||||||
|
etSearch = view.findViewById(R.id.etSearchService);
|
||||||
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
filterServices(s.toString());
|
||||||
|
}
|
||||||
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterServices(String query) {
|
||||||
|
filteredList.clear();
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
filteredList.addAll(serviceList);
|
||||||
|
} else {
|
||||||
|
String lower = query.toLowerCase();
|
||||||
|
for (ServiceDTO s : serviceList) {
|
||||||
|
if (s.getServiceName().toLowerCase().contains(lower)
|
||||||
|
|| s.getServiceDesc().toLowerCase().contains(lower)) {
|
||||||
|
filteredList.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSwipeRefresh(View view) {
|
||||||
|
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshService);
|
||||||
|
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
|
loadServiceData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Open the service detail view depending on the mode
|
||||||
|
private void openServiceDetails(int position) {
|
||||||
|
ServiceDetailFragment detailFragment = new ServiceDetailFragment();
|
||||||
|
|
||||||
|
//Make a bundle to pass data to the detail fragment
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("position", position);
|
||||||
|
|
||||||
|
//if editing a service, add the service data to the bundle
|
||||||
|
if (position != -1) {
|
||||||
|
ServiceDTO service = filteredList.get(position);
|
||||||
|
args.putInt("serviceId", service.getServiceId().intValue());
|
||||||
|
args.putString("serviceName", service.getServiceName());
|
||||||
|
args.putString("serviceDesc", service.getServiceDesc());
|
||||||
|
args.putInt("serviceDuration", service.getServiceDuration());
|
||||||
|
args.putDouble("servicePrice", service.getServicePrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
//send the bundle to the detail fragment to display
|
||||||
|
detailFragment.setArguments(args);
|
||||||
|
//set the service fragment to the parent so we refer back to service view when save or delete is done
|
||||||
|
detailFragment.setServiceFragment(this);
|
||||||
|
|
||||||
|
//get ListFragment to load the the detail view
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.loadFragment(detailFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called by ServiceAdapter when a row is clicked to open the details view
|
||||||
|
@Override
|
||||||
|
public void onServiceClick(int position) {
|
||||||
|
openServiceDetails(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to get a list of all services from the backend
|
||||||
|
private void loadServiceData() {
|
||||||
|
if (swipeRefreshLayout != null) {
|
||||||
|
swipeRefreshLayout.setRefreshing(true);
|
||||||
|
}
|
||||||
|
api.getAllServices(0, 100).enqueue(new Callback<PageResponse<ServiceDTO>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<PageResponse<ServiceDTO>> call, Response<PageResponse<ServiceDTO>> response) {
|
||||||
|
if (swipeRefreshLayout != null) {
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
serviceList.clear();
|
||||||
|
serviceList.addAll(response.body().getContent());
|
||||||
|
filterServices(etSearch.getText().toString());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.e("onResponse: ", response.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<PageResponse<ServiceDTO>> call, Throwable t) {
|
||||||
|
if (swipeRefreshLayout != null) {
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
|
if (getContext() != null) {
|
||||||
|
Toast.makeText(getContext(),
|
||||||
|
"Failed to load services", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
Log.e("onFailure: ", t.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//set up the recyclerview and adapter
|
||||||
|
private void setupRecyclerView(View view) {
|
||||||
|
RecyclerView recyclerView = view.findViewById(R.id.recyclerViewServices);
|
||||||
|
adapter = new ServiceAdapter(filteredList, this);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.adapters.SupplierAdapter;
|
||||||
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
|
import com.example.petstoremobile.api.SupplierApi;
|
||||||
|
import com.example.petstoremobile.dtos.PageResponse;
|
||||||
|
import com.example.petstoremobile.dtos.SupplierDTO;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.detailfragments.SupplierDetailFragment;
|
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class SupplierFragment extends Fragment implements SupplierAdapter.OnSupplierClickListener {
|
||||||
|
|
||||||
|
private List<SupplierDTO> supplierList = new ArrayList<>();
|
||||||
|
private List<SupplierDTO> filteredList = new ArrayList<>();
|
||||||
|
private SupplierAdapter adapter;
|
||||||
|
private ImageButton hamburger;
|
||||||
|
private SupplierApi api;
|
||||||
|
private SwipeRefreshLayout swipeRefreshLayout;
|
||||||
|
private EditText etSearch;
|
||||||
|
|
||||||
|
//load supplier view
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_supplier, container, false);
|
||||||
|
|
||||||
|
api = RetrofitClient.getSupplierApi(requireContext());
|
||||||
|
hamburger = view.findViewById(R.id.btnHamburger);
|
||||||
|
|
||||||
|
setupRecyclerView(view);
|
||||||
|
setupSearch(view);
|
||||||
|
setupSwipeRefresh(view);
|
||||||
|
loadSupplierData();
|
||||||
|
|
||||||
|
//Add button to opens the add dialog
|
||||||
|
FloatingActionButton fabAddSupplier = view.findViewById(R.id.fabAddSupplier);
|
||||||
|
fabAddSupplier.setOnClickListener(v -> openSupplierDetails(-1));
|
||||||
|
|
||||||
|
//Make the hamburger button open the drawer from listFragment
|
||||||
|
hamburger.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
//if list fragment is found then use its helper function to open the drawer
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.openDrawer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSearch(View view) {
|
||||||
|
etSearch = view.findViewById(R.id.etSearchSupplier);
|
||||||
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
filterSuppliers(s.toString());
|
||||||
|
}
|
||||||
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterSuppliers(String query) {
|
||||||
|
filteredList.clear();
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
filteredList.addAll(supplierList);
|
||||||
|
} else {
|
||||||
|
String lower = query.toLowerCase();
|
||||||
|
for (SupplierDTO s : supplierList) {
|
||||||
|
if (s.getSupCompany().toLowerCase().contains(lower)
|
||||||
|
|| s.getSupContactFirstName().toLowerCase().contains(lower)
|
||||||
|
|| s.getSupContactLastName().toLowerCase().contains(lower)) {
|
||||||
|
filteredList.add(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSwipeRefresh(View view) {
|
||||||
|
swipeRefreshLayout = view.findViewById(R.id.swipeRefreshSupplier);
|
||||||
|
swipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
|
loadSupplierData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Open the supplier detail view depending on the mode
|
||||||
|
private void openSupplierDetails(int position) {
|
||||||
|
SupplierDetailFragment detailFragment = new SupplierDetailFragment();
|
||||||
|
|
||||||
|
//Make a bundle to pass data to the detail fragment
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putInt("position", position);
|
||||||
|
|
||||||
|
//if editing a supplier, add the supplier data to the bundle
|
||||||
|
if (position != -1) {
|
||||||
|
SupplierDTO supplier = filteredList.get(position);
|
||||||
|
args.putInt("supId", supplier.getSupId().intValue());
|
||||||
|
args.putString("supCompany", supplier.getSupCompany());
|
||||||
|
args.putString("supContactFirstName", supplier.getSupContactFirstName());
|
||||||
|
args.putString("supContactLastName", supplier.getSupContactLastName());
|
||||||
|
args.putString("supEmail", supplier.getSupEmail());
|
||||||
|
args.putString("supPhone", supplier.getSupPhone());
|
||||||
|
}
|
||||||
|
|
||||||
|
//send the bundle to the detail fragment to display
|
||||||
|
detailFragment.setArguments(args);
|
||||||
|
//set the supplier fragment to the parent so we refer back to supplier view when save or delete is done
|
||||||
|
detailFragment.setSupplierFragment(this);
|
||||||
|
|
||||||
|
//get ListFragment to load the the detail view
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.loadFragment(detailFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Called by SupplierAdapter when a row is clicked to open the details view
|
||||||
|
@Override
|
||||||
|
public void onSupplierClick(int position) {
|
||||||
|
openSupplierDetails(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to get a list of all suppliers from the backend
|
||||||
|
private void loadSupplierData() {
|
||||||
|
if (swipeRefreshLayout != null) {
|
||||||
|
swipeRefreshLayout.setRefreshing(true);
|
||||||
|
}
|
||||||
|
api.getAllSuppliers(0, 100).enqueue(new Callback<PageResponse<SupplierDTO>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<PageResponse<SupplierDTO>> call, Response<PageResponse<SupplierDTO>> response) {
|
||||||
|
if (swipeRefreshLayout != null) {
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
|
supplierList.clear();
|
||||||
|
supplierList.addAll(response.body().getContent());
|
||||||
|
filterSuppliers(etSearch.getText().toString());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.e("onResponse: ", response.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<PageResponse<SupplierDTO>> call, Throwable t) {
|
||||||
|
if (swipeRefreshLayout != null) {
|
||||||
|
swipeRefreshLayout.setRefreshing(false);
|
||||||
|
}
|
||||||
|
if (getContext() != null) {
|
||||||
|
Toast.makeText(getContext(),
|
||||||
|
"Failed to load suppliers", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
Log.e("onFailure: ", t.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//set up the recyclerview and adapter
|
||||||
|
private void setupRecyclerView(View view) {
|
||||||
|
RecyclerView recyclerView = view.findViewById(R.id.recyclerViewSuppliers);
|
||||||
|
adapter = new SupplierAdapter(filteredList, this);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||||
|
|
||||||
|
|
||||||
|
// Uses InputValidator for detailed field validation and ActivityLogger to log all changes.
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.AdoptionFragment;
|
||||||
|
import com.example.petstoremobile.models.Adoption;
|
||||||
|
import com.example.petstoremobile.utils.ActivityLogger;
|
||||||
|
import com.example.petstoremobile.utils.InputValidator;
|
||||||
|
|
||||||
|
public class AdoptionDetailFragment extends Fragment {
|
||||||
|
|
||||||
|
private TextView tvMode, tvAdoptionId;
|
||||||
|
private EditText etAdopterName, etAdopterEmail, etAdopterPhone, etPetName, etAdoptionDate;
|
||||||
|
private Spinner spinnerAdoptionStatus;
|
||||||
|
private Button btnSaveAdoption, btnDeleteAdoption, btnBack;
|
||||||
|
private int adoptionId;
|
||||||
|
private int position;
|
||||||
|
private boolean isEditing = false;
|
||||||
|
private AdoptionFragment adoptionFragment;
|
||||||
|
|
||||||
|
// Set the adoption fragment as parent so we refer back when save or delete is done
|
||||||
|
public void setAdoptionFragment(AdoptionFragment fragment) {
|
||||||
|
this.adoptionFragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_adoption_detail, container, false);
|
||||||
|
|
||||||
|
initViews(view);
|
||||||
|
setupSpinner();
|
||||||
|
handleArguments();
|
||||||
|
|
||||||
|
btnBack.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
});
|
||||||
|
btnSaveAdoption.setOnClickListener(v -> saveAdoption());
|
||||||
|
btnDeleteAdoption.setOnClickListener(v -> deleteAdoption());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates all fields using InputValidator, then saves the adoption record
|
||||||
|
private void saveAdoption() {
|
||||||
|
if (!InputValidator.isNotEmpty(etAdopterName, "Adopter Name")) return;
|
||||||
|
if (!InputValidator.isValidEmail(etAdopterEmail)) return;
|
||||||
|
if (!InputValidator.isValidPhone(etAdopterPhone)) return;
|
||||||
|
if (!InputValidator.isNotEmpty(etPetName, "Pet Name")) return;
|
||||||
|
if (!InputValidator.isValidDate(etAdoptionDate)) return;
|
||||||
|
|
||||||
|
String adopterName = etAdopterName.getText().toString().trim();
|
||||||
|
String adopterEmail = etAdopterEmail.getText().toString().trim();
|
||||||
|
String adopterPhone = etAdopterPhone.getText().toString().trim();
|
||||||
|
String petName = etPetName.getText().toString().trim();
|
||||||
|
String adoptionDate = etAdoptionDate.getText().toString().trim();
|
||||||
|
String status = spinnerAdoptionStatus.getSelectedItem().toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isEditing) {
|
||||||
|
// TODO: Replace with actual API PUT call when backend is ready
|
||||||
|
Adoption updated = new Adoption(adoptionId, adopterName, adopterEmail, adopterPhone, petName, adoptionDate, status);
|
||||||
|
if (adoptionFragment != null) adoptionFragment.onAdoptionSaved(position, updated);
|
||||||
|
ActivityLogger.logChange(requireContext(), "Adoption", "UPDATED", adoptionId);
|
||||||
|
Toast.makeText(getContext(), "Adoption record updated.", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
// TODO: Replace with actual API POST call when backend is ready
|
||||||
|
Adoption newAdoption = new Adoption(0, adopterName, adopterEmail, adopterPhone, petName, adoptionDate, status);
|
||||||
|
if (adoptionFragment != null) adoptionFragment.onAdoptionSaved(-1, newAdoption);
|
||||||
|
ActivityLogger.log(requireContext(), "Added new Adoption record for: " + adopterName + " adopting " + petName);
|
||||||
|
Toast.makeText(getContext(), "Adoption record added.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.logException(requireContext(), "AdoptionDetailFragment.saveAdoption", e);
|
||||||
|
Toast.makeText(getContext(), "Error saving adoption record.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes the adoption record and logs the action
|
||||||
|
private void deleteAdoption() {
|
||||||
|
try {
|
||||||
|
// TODO: Replace with actual API DELETE call when backend is ready
|
||||||
|
if (adoptionFragment != null) adoptionFragment.onAdoptionDeleted(position);
|
||||||
|
ActivityLogger.logChange(requireContext(), "Adoption", "DELETED", adoptionId);
|
||||||
|
Toast.makeText(getContext(), "Adoption record deleted.", Toast.LENGTH_SHORT).show();
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.logException(requireContext(), "AdoptionDetailFragment.deleteAdoption", e);
|
||||||
|
Toast.makeText(getContext(), "Error deleting adoption record.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleArguments() {
|
||||||
|
if (getArguments() != null && getArguments().containsKey("adoptionId")) {
|
||||||
|
isEditing = true;
|
||||||
|
adoptionId = getArguments().getInt("adoptionId");
|
||||||
|
position = getArguments().getInt("position");
|
||||||
|
tvMode.setText("Edit Adoption");
|
||||||
|
tvAdoptionId.setText("ID: " + adoptionId);
|
||||||
|
etAdopterName.setText(getArguments().getString("adopterName"));
|
||||||
|
etAdopterEmail.setText(getArguments().getString("adopterEmail"));
|
||||||
|
etAdopterPhone.setText(getArguments().getString("adopterPhone"));
|
||||||
|
etPetName.setText(getArguments().getString("petName"));
|
||||||
|
etAdoptionDate.setText(getArguments().getString("adoptionDate"));
|
||||||
|
String status = getArguments().getString("status");
|
||||||
|
if ("Approved".equals(status)) spinnerAdoptionStatus.setSelection(0);
|
||||||
|
else if ("Pending".equals(status)) spinnerAdoptionStatus.setSelection(1);
|
||||||
|
else spinnerAdoptionStatus.setSelection(2);
|
||||||
|
btnDeleteAdoption.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
isEditing = false;
|
||||||
|
tvMode.setText("Add Adoption");
|
||||||
|
tvAdoptionId.setVisibility(View.GONE);
|
||||||
|
btnDeleteAdoption.setVisibility(View.GONE);
|
||||||
|
btnSaveAdoption.setText("Add");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initViews(View view) {
|
||||||
|
tvMode = view.findViewById(R.id.tvAdoptionMode);
|
||||||
|
tvAdoptionId = view.findViewById(R.id.tvAdoptionId);
|
||||||
|
etAdopterName = view.findViewById(R.id.etAdopterName);
|
||||||
|
etAdopterEmail = view.findViewById(R.id.etAdopterEmail);
|
||||||
|
etAdopterPhone = view.findViewById(R.id.etAdopterPhone);
|
||||||
|
etPetName = view.findViewById(R.id.etAdoptionPetName);
|
||||||
|
etAdoptionDate = view.findViewById(R.id.etAdoptionDate);
|
||||||
|
spinnerAdoptionStatus = view.findViewById(R.id.spinnerAdoptionStatus);
|
||||||
|
btnSaveAdoption = view.findViewById(R.id.btnSaveAdoption);
|
||||||
|
btnDeleteAdoption = view.findViewById(R.id.btnDeleteAdoption);
|
||||||
|
btnBack = view.findViewById(R.id.btnAdoptionBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSpinner() {
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(requireContext(),
|
||||||
|
android.R.layout.simple_spinner_item,
|
||||||
|
new String[]{"Approved", "Pending", "Rejected"}) {
|
||||||
|
|
||||||
|
//Override the getView method for the spinner to make the text color darker for more readability
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||||
|
View view = super.getView(position, convertView, parent);
|
||||||
|
((TextView) view).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_dark));
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerAdoptionStatus.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||||
|
|
||||||
|
|
||||||
|
// Uses InputValidator for detailed field validation and ActivityLogger to log all changes.
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.AppointmentFragment;
|
||||||
|
import com.example.petstoremobile.models.Appointment;
|
||||||
|
import com.example.petstoremobile.utils.ActivityLogger;
|
||||||
|
import com.example.petstoremobile.utils.InputValidator;
|
||||||
|
|
||||||
|
public class AppointmentDetailFragment extends Fragment {
|
||||||
|
|
||||||
|
private TextView tvMode, tvAppointmentId;
|
||||||
|
private EditText etCustomerName, etPetName, etServiceType, etAppointmentDate, etAppointmentTime;
|
||||||
|
private Spinner spinnerStatus;
|
||||||
|
private Button btnSaveAppointment, btnDeleteAppointment, btnBack;
|
||||||
|
private int appointmentId;
|
||||||
|
private int position;
|
||||||
|
private boolean isEditing = false;
|
||||||
|
private AppointmentFragment appointmentFragment;
|
||||||
|
|
||||||
|
// Set the appointment fragment as parent so we refer back when save or delete is done
|
||||||
|
public void setAppointmentFragment(AppointmentFragment fragment) {
|
||||||
|
this.appointmentFragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_appointment_detail, container, false);
|
||||||
|
|
||||||
|
initViews(view);
|
||||||
|
setupSpinner();
|
||||||
|
handleArguments();
|
||||||
|
|
||||||
|
btnBack.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
btnSaveAppointment.setOnClickListener(v -> saveAppointment());
|
||||||
|
btnDeleteAppointment.setOnClickListener(v -> deleteAppointment());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates all fields using InputValidator, then saves the appointment
|
||||||
|
private void saveAppointment() {
|
||||||
|
// Validate all inputs using InputValidator utility
|
||||||
|
if (!InputValidator.isNotEmpty(etCustomerName, "Customer Name")) return;
|
||||||
|
if (!InputValidator.isNotEmpty(etPetName, "Pet Name")) return;
|
||||||
|
if (!InputValidator.isNotEmpty(etServiceType, "Service Type")) return;
|
||||||
|
if (!InputValidator.isValidDate(etAppointmentDate)) return;
|
||||||
|
if (!InputValidator.isValidTime(etAppointmentTime)) return;
|
||||||
|
|
||||||
|
String customerName = etCustomerName.getText().toString().trim();
|
||||||
|
String petName = etPetName.getText().toString().trim();
|
||||||
|
String serviceType = etServiceType.getText().toString().trim();
|
||||||
|
String date = etAppointmentDate.getText().toString().trim();
|
||||||
|
String time = etAppointmentTime.getText().toString().trim();
|
||||||
|
String status = spinnerStatus.getSelectedItem().toString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isEditing) {
|
||||||
|
// TODO: Replace with actual API PUT call when backend is ready
|
||||||
|
Appointment updated = new Appointment(appointmentId, customerName, petName, serviceType, date, time, status);
|
||||||
|
if (appointmentFragment != null) appointmentFragment.onAppointmentSaved(position, updated);
|
||||||
|
ActivityLogger.logChange(requireContext(), "Appointment", "UPDATED", appointmentId);
|
||||||
|
Toast.makeText(getContext(), "Appointment updated.", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
// TODO: Replace with actual API POST call when backend is ready
|
||||||
|
Appointment newAppt = new Appointment(0, customerName, petName, serviceType, date, time, status);
|
||||||
|
if (appointmentFragment != null) appointmentFragment.onAppointmentSaved(-1, newAppt);
|
||||||
|
ActivityLogger.log(requireContext(), "Added new Appointment for customer: " + customerName);
|
||||||
|
Toast.makeText(getContext(), "Appointment added.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
// Go back to list
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.logException(requireContext(), "AppointmentDetailFragment.saveAppointment", e);
|
||||||
|
Toast.makeText(getContext(), "Error saving appointment.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes the appointment and logs the action
|
||||||
|
private void deleteAppointment() {
|
||||||
|
try {
|
||||||
|
// TODO: Replace with actual API DELETE call when backend is ready
|
||||||
|
if (appointmentFragment != null) appointmentFragment.onAppointmentDeleted(position);
|
||||||
|
ActivityLogger.logChange(requireContext(), "Appointment", "DELETED", appointmentId);
|
||||||
|
Toast.makeText(getContext(), "Appointment deleted.", Toast.LENGTH_SHORT).show();
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.logException(requireContext(), "AppointmentDetailFragment.deleteAppointment", e);
|
||||||
|
Toast.makeText(getContext(), "Error deleting appointment.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if the fragment is in add or edit mode and populates fields accordingly
|
||||||
|
private void handleArguments() {
|
||||||
|
if (getArguments() != null && getArguments().containsKey("appointmentId")) {
|
||||||
|
isEditing = true;
|
||||||
|
appointmentId = getArguments().getInt("appointmentId");
|
||||||
|
position = getArguments().getInt("position");
|
||||||
|
tvMode.setText("Edit Appointment");
|
||||||
|
tvAppointmentId.setText("ID: " + appointmentId);
|
||||||
|
etCustomerName.setText(getArguments().getString("customerName"));
|
||||||
|
etPetName.setText(getArguments().getString("petName"));
|
||||||
|
etServiceType.setText(getArguments().getString("serviceType"));
|
||||||
|
etAppointmentDate.setText(getArguments().getString("appointmentDate"));
|
||||||
|
etAppointmentTime.setText(getArguments().getString("appointmentTime"));
|
||||||
|
String status = getArguments().getString("status");
|
||||||
|
if ("Confirmed".equals(status)) spinnerStatus.setSelection(0);
|
||||||
|
else if ("Pending".equals(status)) spinnerStatus.setSelection(1);
|
||||||
|
else spinnerStatus.setSelection(2);
|
||||||
|
btnDeleteAppointment.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
isEditing = false;
|
||||||
|
tvMode.setText("Add Appointment");
|
||||||
|
tvAppointmentId.setVisibility(View.GONE);
|
||||||
|
btnDeleteAppointment.setVisibility(View.GONE);
|
||||||
|
btnSaveAppointment.setText("Add");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initViews(View view) {
|
||||||
|
tvMode = view.findViewById(R.id.tvApptMode);
|
||||||
|
tvAppointmentId = view.findViewById(R.id.tvAppointmentId);
|
||||||
|
etCustomerName = view.findViewById(R.id.etCustomerName);
|
||||||
|
etPetName = view.findViewById(R.id.etApptPetName);
|
||||||
|
etServiceType = view.findViewById(R.id.etServiceType);
|
||||||
|
etAppointmentDate = view.findViewById(R.id.etAppointmentDate);
|
||||||
|
etAppointmentTime = view.findViewById(R.id.etAppointmentTime);
|
||||||
|
spinnerStatus = view.findViewById(R.id.spinnerAppointmentStatus);
|
||||||
|
btnSaveAppointment = view.findViewById(R.id.btnSaveAppointment);
|
||||||
|
btnDeleteAppointment = view.findViewById(R.id.btnDeleteAppointment);
|
||||||
|
btnBack = view.findViewById(R.id.btnApptBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSpinner() {
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(requireContext(),
|
||||||
|
android.R.layout.simple_spinner_item,
|
||||||
|
new String[]{"Confirmed", "Pending", "Cancelled"}) {
|
||||||
|
|
||||||
|
//Override the getView method for the spinner to make the text color darker for more readability
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||||
|
View view = super.getView(position, convertView, parent);
|
||||||
|
((TextView) view).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_dark));
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerStatus.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||||
|
|
||||||
|
// Uses InputValidator for detailed field validation and ActivityLogger to log all changes.
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.InventoryFragment;
|
||||||
|
import com.example.petstoremobile.models.Inventory;
|
||||||
|
import com.example.petstoremobile.utils.ActivityLogger;
|
||||||
|
import com.example.petstoremobile.utils.InputValidator;
|
||||||
|
|
||||||
|
public class InventoryDetailFragment extends Fragment {
|
||||||
|
|
||||||
|
private TextView tvMode, tvInventoryId;
|
||||||
|
private EditText etItemName, etCategory, etQuantity, etUnitPrice, etSupplier;
|
||||||
|
private Button btnSaveInventory, btnDeleteInventory, btnBack;
|
||||||
|
private int inventoryId;
|
||||||
|
private int position;
|
||||||
|
private boolean isEditing = false;
|
||||||
|
private InventoryFragment inventoryFragment;
|
||||||
|
|
||||||
|
// Set the inventory fragment as parent so we refer back when save or delete is done
|
||||||
|
public void setInventoryFragment(InventoryFragment fragment) {
|
||||||
|
this.inventoryFragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_inventory_detail, container, false);
|
||||||
|
|
||||||
|
initViews(view);
|
||||||
|
handleArguments();
|
||||||
|
|
||||||
|
btnBack.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
});
|
||||||
|
btnSaveInventory.setOnClickListener(v -> saveInventory());
|
||||||
|
btnDeleteInventory.setOnClickListener(v -> deleteInventory());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates all fields using InputValidator, then saves the inventory item
|
||||||
|
private void saveInventory() {
|
||||||
|
if (!InputValidator.isNotEmpty(etItemName, "Item Name")) return;
|
||||||
|
if (!InputValidator.isNotEmpty(etCategory, "Category")) return;
|
||||||
|
if (!InputValidator.isPositiveInteger(etQuantity, "Quantity")) return;
|
||||||
|
if (!InputValidator.isPositiveDecimal(etUnitPrice, "Unit Price")) return;
|
||||||
|
if (!InputValidator.isNotEmpty(etSupplier, "Supplier")) return;
|
||||||
|
|
||||||
|
String itemName = etItemName.getText().toString().trim();
|
||||||
|
String category = etCategory.getText().toString().trim();
|
||||||
|
int quantity = Integer.parseInt(etQuantity.getText().toString().trim());
|
||||||
|
double unitPrice = Double.parseDouble(etUnitPrice.getText().toString().trim());
|
||||||
|
String supplier = etSupplier.getText().toString().trim();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isEditing) {
|
||||||
|
// TODO: Replace with actual API PUT call when backend is ready
|
||||||
|
Inventory updated = new Inventory(inventoryId, itemName, category, quantity, unitPrice, supplier);
|
||||||
|
if (inventoryFragment != null) inventoryFragment.onInventorySaved(position, updated);
|
||||||
|
ActivityLogger.logChange(requireContext(), "Inventory", "UPDATED", inventoryId);
|
||||||
|
Toast.makeText(getContext(), "Inventory item updated.", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
// TODO: Replace with actual API POST call when backend is ready
|
||||||
|
Inventory newItem = new Inventory(0, itemName, category, quantity, unitPrice, supplier);
|
||||||
|
if (inventoryFragment != null) inventoryFragment.onInventorySaved(-1, newItem);
|
||||||
|
ActivityLogger.log(requireContext(), "Added new Inventory item: " + itemName);
|
||||||
|
Toast.makeText(getContext(), "Inventory item added.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.logException(requireContext(), "InventoryDetailFragment.saveInventory", e);
|
||||||
|
Toast.makeText(getContext(), "Error saving inventory item.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes the inventory item and logs the action
|
||||||
|
private void deleteInventory() {
|
||||||
|
try {
|
||||||
|
// TODO: Replace with actual API DELETE call when backend is ready
|
||||||
|
if (inventoryFragment != null) inventoryFragment.onInventoryDeleted(position);
|
||||||
|
ActivityLogger.logChange(requireContext(), "Inventory", "DELETED", inventoryId);
|
||||||
|
Toast.makeText(getContext(), "Inventory item deleted.", Toast.LENGTH_SHORT).show();
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.logException(requireContext(), "InventoryDetailFragment.deleteInventory", e);
|
||||||
|
Toast.makeText(getContext(), "Error deleting inventory item.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleArguments() {
|
||||||
|
if (getArguments() != null && getArguments().containsKey("inventoryId")) {
|
||||||
|
isEditing = true;
|
||||||
|
inventoryId = getArguments().getInt("inventoryId");
|
||||||
|
position = getArguments().getInt("position");
|
||||||
|
tvMode.setText("Edit Inventory Item");
|
||||||
|
tvInventoryId.setText("ID: " + inventoryId);
|
||||||
|
etItemName.setText(getArguments().getString("itemName"));
|
||||||
|
etCategory.setText(getArguments().getString("category"));
|
||||||
|
etQuantity.setText(String.valueOf(getArguments().getInt("quantity")));
|
||||||
|
etUnitPrice.setText(String.valueOf(getArguments().getDouble("unitPrice")));
|
||||||
|
etSupplier.setText(getArguments().getString("supplier"));
|
||||||
|
btnDeleteInventory.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
isEditing = false;
|
||||||
|
tvMode.setText("Add Inventory Item");
|
||||||
|
tvInventoryId.setVisibility(View.GONE);
|
||||||
|
btnDeleteInventory.setVisibility(View.GONE);
|
||||||
|
btnSaveInventory.setText("Add");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initViews(View view) {
|
||||||
|
tvMode = view.findViewById(R.id.tvInventoryMode);
|
||||||
|
tvInventoryId = view.findViewById(R.id.tvInventoryId);
|
||||||
|
etItemName = view.findViewById(R.id.etItemName);
|
||||||
|
etCategory = view.findViewById(R.id.etInventoryCategory);
|
||||||
|
etQuantity = view.findViewById(R.id.etQuantity);
|
||||||
|
etUnitPrice = view.findViewById(R.id.etUnitPrice);
|
||||||
|
etSupplier = view.findViewById(R.id.etInventorySupplier);
|
||||||
|
btnSaveInventory = view.findViewById(R.id.btnSaveInventory);
|
||||||
|
btnDeleteInventory = view.findViewById(R.id.btnDeleteInventory);
|
||||||
|
btnBack = view.findViewById(R.id.btnInventoryBack);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.api.PetApi;
|
||||||
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
|
import com.example.petstoremobile.dtos.PetDTO;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.PetFragment;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class PetDetailFragment extends Fragment {
|
||||||
|
|
||||||
|
private TextView tvMode, tvPetId;
|
||||||
|
private EditText etPetName, etPetSpecies, etPetBreed, etPetAge, etPetPrice;
|
||||||
|
private Spinner spinnerPetStatus;
|
||||||
|
private Button btnSavePet, btnDeletePet, btnBack;
|
||||||
|
private int petId;
|
||||||
|
private boolean isEditing = false;
|
||||||
|
private PetFragment petFragment;
|
||||||
|
|
||||||
|
//set the pet fragment to the parent so we refer back to pet view when save or delete is done
|
||||||
|
public void setPetFragment(PetFragment fragment) {
|
||||||
|
this.petFragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_pet_detail, container, false);
|
||||||
|
|
||||||
|
//set up spinner and get controls from layout and display the view depending on the mode
|
||||||
|
initViews(view);
|
||||||
|
setupSpinner();
|
||||||
|
handleArguments();
|
||||||
|
|
||||||
|
//set button click listeners
|
||||||
|
btnBack.setOnClickListener(v -> navigateBack());
|
||||||
|
btnSavePet.setOnClickListener(v -> savePet());
|
||||||
|
btnDeletePet.setOnClickListener(v -> deletePet());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Method to Update or Add a pet
|
||||||
|
private void savePet() {
|
||||||
|
//get all the values from the fields
|
||||||
|
String name = etPetName.getText().toString().trim();
|
||||||
|
String species = etPetSpecies.getText().toString().trim();
|
||||||
|
String breed = etPetBreed.getText().toString().trim();
|
||||||
|
String ageStr = etPetAge.getText().toString().trim();
|
||||||
|
String priceStr = etPetPrice.getText().toString().trim();
|
||||||
|
String status = spinnerPetStatus.getSelectedItem().toString();
|
||||||
|
|
||||||
|
//check if all the fields are filled
|
||||||
|
if (name.isEmpty() || species.isEmpty() || breed.isEmpty() || ageStr.isEmpty() || priceStr.isEmpty()) {
|
||||||
|
Toast.makeText(getContext(), "Please fill in all fields", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create a pet object to send to the API
|
||||||
|
PetDTO petDTO = new PetDTO();
|
||||||
|
petDTO.setPetName(name);
|
||||||
|
petDTO.setPetSpecies(species);
|
||||||
|
petDTO.setPetBreed(breed);
|
||||||
|
petDTO.setPetAge(Integer.parseInt(ageStr));
|
||||||
|
petDTO.setPetPrice(priceStr);
|
||||||
|
petDTO.setPetStatus(status);
|
||||||
|
|
||||||
|
PetApi petApi = RetrofitClient.getPetApi(requireContext());
|
||||||
|
|
||||||
|
//check if the pet is being edited or added
|
||||||
|
if (isEditing) {
|
||||||
|
// Update existing pet
|
||||||
|
petDTO.setPetId((long) petId);
|
||||||
|
petApi.updatePet((long) petId, petDTO).enqueue(new Callback<PetDTO>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<PetDTO> call, Response<PetDTO> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Toast.makeText(getContext(), "Pet updated successfully!", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateBack();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to update pet: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<PetDTO> call, Throwable t) {
|
||||||
|
Log.e("PetDetailFragment", "Error updating pet", t);
|
||||||
|
Toast.makeText(getContext(), "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Add new pet
|
||||||
|
petApi.createPet(petDTO).enqueue(new Callback<PetDTO>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<PetDTO> call, Response<PetDTO> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Toast.makeText(getContext(), "Pet added successfully!", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateBack();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to add pet: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<PetDTO> call, Throwable t) {
|
||||||
|
Log.e("PetDetailFragment", "Error adding pet", t);
|
||||||
|
Toast.makeText(getContext(), "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Method to Delete a pet
|
||||||
|
private void deletePet() {
|
||||||
|
//Alert the user to confirm the delete
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Delete Pet")
|
||||||
|
.setMessage("Are you sure you want to delete " + etPetName.getText().toString() + "?")
|
||||||
|
.setPositiveButton("Delete", (dialog, which) -> {
|
||||||
|
PetApi petApi = RetrofitClient.getPetApi(requireContext());
|
||||||
|
//if they say yes then delete the pet
|
||||||
|
petApi.deletePet((long) petId).enqueue(new Callback<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<Void> call, Response<Void> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Toast.makeText(getContext(), "Pet deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateBack();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to delete pet: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<Void> call, Throwable t) {
|
||||||
|
Log.e("PetDetailFragment", "Error deleting pet", t);
|
||||||
|
Toast.makeText(getContext(), "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancel", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper method to navigate back to the list
|
||||||
|
private void navigateBack() {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
// If editing pop back twice to get back to PetDetail Fragment instead of PetProfileFragment
|
||||||
|
if (isEditing) {
|
||||||
|
listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to check if pet is being edited or added and show the view accordingly
|
||||||
|
private void handleArguments() {
|
||||||
|
// Pet is being edited if the bundle contains a petId
|
||||||
|
if (getArguments() != null && getArguments().containsKey("petId")) {
|
||||||
|
// Get pet data from arguments and populate fields
|
||||||
|
isEditing = true;
|
||||||
|
petId = getArguments().getInt("petId");
|
||||||
|
tvMode.setText("Edit Pet");
|
||||||
|
tvPetId.setText("ID: " + petId);
|
||||||
|
etPetName.setText(getArguments().getString("petName"));
|
||||||
|
etPetSpecies.setText(getArguments().getString("petSpecies"));
|
||||||
|
etPetBreed.setText(getArguments().getString("petBreed"));
|
||||||
|
etPetAge.setText(String.valueOf(getArguments().getInt("petAge")));
|
||||||
|
etPetPrice.setText(String.valueOf(getArguments().getDouble("petPrice")));
|
||||||
|
String status = getArguments().getString("petStatus");
|
||||||
|
if ("Available".equals(status)) {
|
||||||
|
spinnerPetStatus.setSelection(0);
|
||||||
|
} else {
|
||||||
|
spinnerPetStatus.setSelection(1);
|
||||||
|
}
|
||||||
|
btnDeletePet.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
// Pet is being added
|
||||||
|
// Set default values for add a new pet
|
||||||
|
isEditing = false;
|
||||||
|
tvMode.setText("Add Pet");
|
||||||
|
tvPetId.setVisibility(View.GONE);
|
||||||
|
btnDeletePet.setVisibility(View.GONE);
|
||||||
|
btnSavePet.setText("Add");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to get controls from layout
|
||||||
|
private void initViews(View view) {
|
||||||
|
tvMode = view.findViewById(R.id.tvMode);
|
||||||
|
tvPetId = view.findViewById(R.id.tvPetId);
|
||||||
|
etPetName = view.findViewById(R.id.etPetName);
|
||||||
|
etPetSpecies = view.findViewById(R.id.etPetSpecies);
|
||||||
|
etPetBreed = view.findViewById(R.id.etPetBreed);
|
||||||
|
etPetAge = view.findViewById(R.id.etPetAge);
|
||||||
|
etPetPrice = view.findViewById(R.id.etPetPrice);
|
||||||
|
spinnerPetStatus = view.findViewById(R.id.spinnerPetStatus);
|
||||||
|
btnSavePet = view.findViewById(R.id.btnSavePet);
|
||||||
|
btnDeletePet = view.findViewById(R.id.btnDeletePet);
|
||||||
|
btnBack = view.findViewById(R.id.btnBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to set up the spinner menu for pet status
|
||||||
|
private void setupSpinner() {
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(requireContext(),
|
||||||
|
android.R.layout.simple_spinner_item,
|
||||||
|
new String[]{"Available", "Adopted"}) {
|
||||||
|
|
||||||
|
//Override the getView method for the spinner to make the text color darker for more readability
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
|
||||||
|
View view = super.getView(position, convertView, parent);
|
||||||
|
((TextView) view).setTextColor(ContextCompat.getColor(requireContext(), R.color.text_dark));
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinnerPetStatus.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||||
|
|
||||||
|
// Uses InputValidator for detailed field validation and ActivityLogger to log all changes.
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.ProductFragment;
|
||||||
|
import com.example.petstoremobile.models.Product;
|
||||||
|
import com.example.petstoremobile.utils.ActivityLogger;
|
||||||
|
import com.example.petstoremobile.utils.InputValidator;
|
||||||
|
|
||||||
|
public class ProductDetailFragment extends Fragment {
|
||||||
|
|
||||||
|
private TextView tvMode, tvProductId;
|
||||||
|
private EditText etProductName, etProductDesc, etCategory, etProductPrice, etStockQuantity;
|
||||||
|
private Button btnSaveProduct, btnDeleteProduct, btnBack;
|
||||||
|
private int productId;
|
||||||
|
private int position;
|
||||||
|
private boolean isEditing = false;
|
||||||
|
private ProductFragment productFragment;
|
||||||
|
|
||||||
|
// Set the product fragment as parent so we refer back when save or delete is done
|
||||||
|
public void setProductFragment(ProductFragment fragment) {
|
||||||
|
this.productFragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_product_detail, container, false);
|
||||||
|
|
||||||
|
initViews(view);
|
||||||
|
handleArguments();
|
||||||
|
|
||||||
|
btnBack.setOnClickListener(v -> {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
});
|
||||||
|
btnSaveProduct.setOnClickListener(v -> saveProduct());
|
||||||
|
btnDeleteProduct.setOnClickListener(v -> deleteProduct());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates all fields using InputValidator, then saves the product
|
||||||
|
private void saveProduct() {
|
||||||
|
if (!InputValidator.isNotEmpty(etProductName, "Product Name")) return;
|
||||||
|
if (!InputValidator.isNotEmpty(etProductDesc, "Description")) return;
|
||||||
|
if (!InputValidator.isNotEmpty(etCategory, "Category")) return;
|
||||||
|
if (!InputValidator.isPositiveDecimal(etProductPrice, "Price")) return;
|
||||||
|
if (!InputValidator.isPositiveInteger(etStockQuantity, "Stock Quantity")) return;
|
||||||
|
|
||||||
|
String productName = etProductName.getText().toString().trim();
|
||||||
|
String productDesc = etProductDesc.getText().toString().trim();
|
||||||
|
String category = etCategory.getText().toString().trim();
|
||||||
|
double productPrice = Double.parseDouble(etProductPrice.getText().toString().trim());
|
||||||
|
int stockQuantity = Integer.parseInt(etStockQuantity.getText().toString().trim());
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isEditing) {
|
||||||
|
// TODO: Replace with actual API PUT call when backend is ready
|
||||||
|
Product updated = new Product(productId, productName, productDesc, category, productPrice, stockQuantity);
|
||||||
|
if (productFragment != null) productFragment.onProductSaved(position, updated);
|
||||||
|
ActivityLogger.logChange(requireContext(), "Product", "UPDATED", productId);
|
||||||
|
Toast.makeText(getContext(), "Product updated.", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
// TODO: Replace with actual API POST call when backend is ready
|
||||||
|
Product newProduct = new Product(0, productName, productDesc, category, productPrice, stockQuantity);
|
||||||
|
if (productFragment != null) productFragment.onProductSaved(-1, newProduct);
|
||||||
|
ActivityLogger.log(requireContext(), "Added new Product: " + productName);
|
||||||
|
Toast.makeText(getContext(), "Product added.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.logException(requireContext(), "ProductDetailFragment.saveProduct", e);
|
||||||
|
Toast.makeText(getContext(), "Error saving product.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deletes the product and logs the action
|
||||||
|
private void deleteProduct() {
|
||||||
|
try {
|
||||||
|
// TODO: Replace with actual API DELETE call when backend is ready
|
||||||
|
if (productFragment != null) productFragment.onProductDeleted(position);
|
||||||
|
ActivityLogger.logChange(requireContext(), "Product", "DELETED", productId);
|
||||||
|
Toast.makeText(getContext(), "Product deleted.", Toast.LENGTH_SHORT).show();
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ActivityLogger.logException(requireContext(), "ProductDetailFragment.deleteProduct", e);
|
||||||
|
Toast.makeText(getContext(), "Error deleting product.", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleArguments() {
|
||||||
|
if (getArguments() != null && getArguments().containsKey("productId")) {
|
||||||
|
isEditing = true;
|
||||||
|
productId = getArguments().getInt("productId");
|
||||||
|
position = getArguments().getInt("position");
|
||||||
|
tvMode.setText("Edit Product");
|
||||||
|
tvProductId.setText("ID: " + productId);
|
||||||
|
etProductName.setText(getArguments().getString("productName"));
|
||||||
|
etProductDesc.setText(getArguments().getString("productDesc"));
|
||||||
|
etCategory.setText(getArguments().getString("category"));
|
||||||
|
etProductPrice.setText(String.valueOf(getArguments().getDouble("productPrice")));
|
||||||
|
etStockQuantity.setText(String.valueOf(getArguments().getInt("stockQuantity")));
|
||||||
|
btnDeleteProduct.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
isEditing = false;
|
||||||
|
tvMode.setText("Add Product");
|
||||||
|
tvProductId.setVisibility(View.GONE);
|
||||||
|
btnDeleteProduct.setVisibility(View.GONE);
|
||||||
|
btnSaveProduct.setText("Add");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initViews(View view) {
|
||||||
|
tvMode = view.findViewById(R.id.tvProductMode);
|
||||||
|
tvProductId = view.findViewById(R.id.tvProductId);
|
||||||
|
etProductName = view.findViewById(R.id.etProductName);
|
||||||
|
etProductDesc = view.findViewById(R.id.etProductDesc);
|
||||||
|
etCategory = view.findViewById(R.id.etProductCategory);
|
||||||
|
etProductPrice = view.findViewById(R.id.etProductPrice);
|
||||||
|
etStockQuantity = view.findViewById(R.id.etStockQuantity);
|
||||||
|
btnSaveProduct = view.findViewById(R.id.btnSaveProduct);
|
||||||
|
btnDeleteProduct = view.findViewById(R.id.btnDeleteProduct);
|
||||||
|
btnBack = view.findViewById(R.id.btnProductBack);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
|
import com.example.petstoremobile.api.ServiceApi;
|
||||||
|
import com.example.petstoremobile.dtos.ServiceDTO;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.ServiceFragment;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class ServiceDetailFragment extends Fragment {
|
||||||
|
|
||||||
|
private TextView tvMode, tvServiceId;
|
||||||
|
private EditText etServiceName, etServiceDesc, etServiceDuration, etServicePrice;
|
||||||
|
private Button btnSaveService, btnDeleteService, btnBack;
|
||||||
|
private int serviceId;
|
||||||
|
private boolean isEditing = false;
|
||||||
|
private ServiceFragment serviceFragment;
|
||||||
|
|
||||||
|
//set the service fragment to the parent so we refer back to service view when save or delete is done
|
||||||
|
public void setServiceFragment(ServiceFragment fragment) {
|
||||||
|
this.serviceFragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_service_detail, container, false);
|
||||||
|
|
||||||
|
//get controls from layout and display the view depending on the mode
|
||||||
|
initViews(view);
|
||||||
|
handleArguments();
|
||||||
|
|
||||||
|
//set button click listeners
|
||||||
|
btnBack.setOnClickListener(v -> navigateBack());
|
||||||
|
btnSaveService.setOnClickListener(v -> saveService());
|
||||||
|
btnDeleteService.setOnClickListener(v -> deleteService());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Method to Update or Add a service
|
||||||
|
private void saveService() {
|
||||||
|
//get all the values from the fields
|
||||||
|
String name = etServiceName.getText().toString().trim();
|
||||||
|
String desc = etServiceDesc.getText().toString().trim();
|
||||||
|
String durationStr = etServiceDuration.getText().toString().trim();
|
||||||
|
String priceStr = etServicePrice.getText().toString().trim();
|
||||||
|
|
||||||
|
//check if all the fields are filled (desc is optional)
|
||||||
|
if (name.isEmpty() || durationStr.isEmpty() || priceStr.isEmpty()) {
|
||||||
|
Toast.makeText(getContext(), "Please fill in all fields", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create a service object to send to the API
|
||||||
|
ServiceDTO serviceDTO = new ServiceDTO();
|
||||||
|
serviceDTO.setServiceName(name);
|
||||||
|
serviceDTO.setServiceDesc(desc);
|
||||||
|
serviceDTO.setServiceDuration(Integer.parseInt(durationStr));
|
||||||
|
serviceDTO.setServicePrice(Double.parseDouble(priceStr));
|
||||||
|
|
||||||
|
ServiceApi serviceApi = RetrofitClient.getServiceApi(requireContext());
|
||||||
|
|
||||||
|
//check if the service is being edited or added
|
||||||
|
if (isEditing) {
|
||||||
|
// Update existing service
|
||||||
|
serviceDTO.setServiceId((long) serviceId);
|
||||||
|
serviceApi.updateService((long) serviceId, serviceDTO).enqueue(new Callback<ServiceDTO>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<ServiceDTO> call, Response<ServiceDTO> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Toast.makeText(getContext(), "Service updated successfully!", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateBack();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to update service: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<ServiceDTO> call, Throwable t) {
|
||||||
|
Log.e("ServiceDetailFragment", "Error updating service", t);
|
||||||
|
Toast.makeText(getContext(), "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Add new service
|
||||||
|
serviceApi.createService(serviceDTO).enqueue(new Callback<ServiceDTO>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<ServiceDTO> call, Response<ServiceDTO> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Toast.makeText(getContext(), "Service added successfully!", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateBack();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to add service: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<ServiceDTO> call, Throwable t) {
|
||||||
|
Log.e("ServiceDetailFragment", "Error adding service", t);
|
||||||
|
Toast.makeText(getContext(), "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Method to Delete a service
|
||||||
|
private void deleteService() {
|
||||||
|
//Alert the user to confirm the delete
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Delete Service")
|
||||||
|
.setMessage("Are you sure you want to delete " + etServiceName.getText().toString() + "?")
|
||||||
|
.setPositiveButton("Delete", (dialog, which) -> {
|
||||||
|
ServiceApi serviceApi = RetrofitClient.getServiceApi(requireContext());
|
||||||
|
serviceApi.deleteService((long) serviceId).enqueue(new Callback<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<Void> call, Response<Void> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Toast.makeText(getContext(), "Service deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateBack();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to delete service: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<Void> call, Throwable t) {
|
||||||
|
Log.e("ServiceDetailFragment", "Error deleting service", t);
|
||||||
|
Toast.makeText(getContext(), "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancel", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper method to navigate back to the list
|
||||||
|
private void navigateBack() {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to check if service is being edited or added and show the view accordingly
|
||||||
|
private void handleArguments() {
|
||||||
|
// Service is being edited if the bundle contains a serviceId
|
||||||
|
if (getArguments() != null && getArguments().containsKey("serviceId")) {
|
||||||
|
// Get service data from arguments and populate fields
|
||||||
|
isEditing = true;
|
||||||
|
serviceId = getArguments().getInt("serviceId");
|
||||||
|
tvMode.setText("Edit Service");
|
||||||
|
tvServiceId.setText("ID: " + serviceId);
|
||||||
|
etServiceName.setText(getArguments().getString("serviceName"));
|
||||||
|
etServiceDesc.setText(getArguments().getString("serviceDesc"));
|
||||||
|
etServiceDuration.setText(String.valueOf(getArguments().getInt("serviceDuration")));
|
||||||
|
etServicePrice.setText(String.valueOf(getArguments().getDouble("servicePrice")));
|
||||||
|
btnDeleteService.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
// Service is being added
|
||||||
|
// Set default values for add a new service
|
||||||
|
isEditing = false;
|
||||||
|
tvMode.setText("Add Service");
|
||||||
|
tvServiceId.setVisibility(View.GONE);
|
||||||
|
btnDeleteService.setVisibility(View.GONE);
|
||||||
|
btnSaveService.setText("Add");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to get controls from layout
|
||||||
|
private void initViews(View view) {
|
||||||
|
tvMode = view.findViewById(R.id.tvMode);
|
||||||
|
tvServiceId = view.findViewById(R.id.tvServiceId);
|
||||||
|
etServiceName = view.findViewById(R.id.etServiceName);
|
||||||
|
etServiceDesc = view.findViewById(R.id.etServiceDesc);
|
||||||
|
etServiceDuration = view.findViewById(R.id.etServiceDuration);
|
||||||
|
etServicePrice = view.findViewById(R.id.etServicePrice);
|
||||||
|
btnSaveService = view.findViewById(R.id.btnSaveService);
|
||||||
|
btnDeleteService = view.findViewById(R.id.btnDeleteService);
|
||||||
|
btnBack = view.findViewById(R.id.btnBack);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments.detailfragments;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
|
import com.example.petstoremobile.api.SupplierApi;
|
||||||
|
import com.example.petstoremobile.dtos.SupplierDTO;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.SupplierFragment;
|
||||||
|
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
public class SupplierDetailFragment extends Fragment {
|
||||||
|
|
||||||
|
private TextView tvMode, tvSupId;
|
||||||
|
private EditText etSupCompany, etSupContactFirstName, etSupContactLastName, etSupEmail, etSupPhone;
|
||||||
|
private Button btnSaveSupplier, btnDeleteSupplier, btnBack;
|
||||||
|
private int supId;
|
||||||
|
private boolean isEditing = false;
|
||||||
|
private SupplierFragment supplierFragment;
|
||||||
|
|
||||||
|
//set the supplier fragment to the parent so we refer back to supplier view when save or delete is done
|
||||||
|
public void setSupplierFragment(SupplierFragment fragment) {
|
||||||
|
this.supplierFragment = fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_supplier_detail, container, false);
|
||||||
|
|
||||||
|
//get controls from layout and display the view depending on the mode
|
||||||
|
initViews(view);
|
||||||
|
handleArguments();
|
||||||
|
|
||||||
|
//set button click listeners
|
||||||
|
btnBack.setOnClickListener(v -> navigateBack());
|
||||||
|
btnSaveSupplier.setOnClickListener(v -> saveSupplier());
|
||||||
|
btnDeleteSupplier.setOnClickListener(v -> deleteSupplier());
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Method to Update or Add a supplier
|
||||||
|
private void saveSupplier() {
|
||||||
|
//get all the values from the fields
|
||||||
|
String company = etSupCompany.getText().toString().trim();
|
||||||
|
String firstName = etSupContactFirstName.getText().toString().trim();
|
||||||
|
String lastName = etSupContactLastName.getText().toString().trim();
|
||||||
|
String email = etSupEmail.getText().toString().trim();
|
||||||
|
String phone = etSupPhone.getText().toString().trim();
|
||||||
|
|
||||||
|
//check if all the fields are filled
|
||||||
|
if (company.isEmpty() || firstName.isEmpty() || lastName.isEmpty() || email.isEmpty() || phone.isEmpty()) {
|
||||||
|
Toast.makeText(getContext(), "Please fill in all fields", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//create a supplier object to send to the API
|
||||||
|
SupplierDTO supplierDTO = new SupplierDTO();
|
||||||
|
supplierDTO.setSupCompany(company);
|
||||||
|
supplierDTO.setSupContactFirstName(firstName);
|
||||||
|
supplierDTO.setSupContactLastName(lastName);
|
||||||
|
supplierDTO.setSupEmail(email);
|
||||||
|
supplierDTO.setSupPhone(phone);
|
||||||
|
|
||||||
|
SupplierApi supplierApi = RetrofitClient.getSupplierApi(requireContext());
|
||||||
|
|
||||||
|
//check if the supplier is being edited or added
|
||||||
|
if (isEditing) {
|
||||||
|
// Update existing supplier
|
||||||
|
supplierDTO.setSupId((long) supId);
|
||||||
|
supplierApi.updateSupplier((long) supId, supplierDTO).enqueue(new Callback<SupplierDTO>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<SupplierDTO> call, Response<SupplierDTO> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Toast.makeText(getContext(), "Supplier updated successfully!", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateBack();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to update supplier: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<SupplierDTO> call, Throwable t) {
|
||||||
|
Log.e("SupplierDetailFragment", "Error updating supplier", t);
|
||||||
|
Toast.makeText(getContext(), "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Add new supplier
|
||||||
|
supplierApi.createSupplier(supplierDTO).enqueue(new Callback<SupplierDTO>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<SupplierDTO> call, Response<SupplierDTO> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Toast.makeText(getContext(), "Supplier added successfully!", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateBack();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to add supplier: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<SupplierDTO> call, Throwable t) {
|
||||||
|
Log.e("SupplierDetailFragment", "Error adding supplier", t);
|
||||||
|
Toast.makeText(getContext(), "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Method to Delete a supplier
|
||||||
|
private void deleteSupplier() {
|
||||||
|
//Alert the user to confirm the delete
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Delete Supplier")
|
||||||
|
.setMessage("Are you sure you want to delete " + etSupCompany.getText().toString() + "?")
|
||||||
|
.setPositiveButton("Delete", (dialog, which) -> {
|
||||||
|
SupplierApi supplierApi = RetrofitClient.getSupplierApi(requireContext());
|
||||||
|
supplierApi.deleteSupplier((long) supId).enqueue(new Callback<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Call<Void> call, Response<Void> response) {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
Toast.makeText(getContext(), "Supplier deleted successfully!", Toast.LENGTH_SHORT).show();
|
||||||
|
navigateBack();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getContext(), "Failed to delete supplier: " + response.code(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Call<Void> call, Throwable t) {
|
||||||
|
Log.e("SupplierDetailFragment", "Error deleting supplier", t);
|
||||||
|
Toast.makeText(getContext(), "Error: " + t.getMessage(), Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancel", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Helper method to navigate back to the list
|
||||||
|
private void navigateBack() {
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to check if supplier is being edited or added and show the view accordingly
|
||||||
|
private void handleArguments() {
|
||||||
|
// Supplier is being edited if the bundle contains a supId
|
||||||
|
if (getArguments() != null && getArguments().containsKey("supId")) {
|
||||||
|
// Get supplier data from arguments and populate fields
|
||||||
|
isEditing = true;
|
||||||
|
supId = getArguments().getInt("supId");
|
||||||
|
tvMode.setText("Edit Supplier");
|
||||||
|
tvSupId.setText("ID: " + supId);
|
||||||
|
etSupCompany.setText(getArguments().getString("supCompany"));
|
||||||
|
etSupContactFirstName.setText(getArguments().getString("supContactFirstName"));
|
||||||
|
etSupContactLastName.setText(getArguments().getString("supContactLastName"));
|
||||||
|
etSupEmail.setText(getArguments().getString("supEmail"));
|
||||||
|
etSupPhone.setText(getArguments().getString("supPhone"));
|
||||||
|
btnDeleteSupplier.setVisibility(View.VISIBLE);
|
||||||
|
} else {
|
||||||
|
// Supplier is being added
|
||||||
|
// Set default values for add a new supplier
|
||||||
|
isEditing = false;
|
||||||
|
tvMode.setText("Add Supplier");
|
||||||
|
tvSupId.setVisibility(View.GONE);
|
||||||
|
btnDeleteSupplier.setVisibility(View.GONE);
|
||||||
|
btnSaveSupplier.setText("Add");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to get controls from layout
|
||||||
|
private void initViews(View view) {
|
||||||
|
tvMode = view.findViewById(R.id.tvMode);
|
||||||
|
tvSupId = view.findViewById(R.id.tvSupId);
|
||||||
|
etSupCompany = view.findViewById(R.id.etSupCompany);
|
||||||
|
etSupContactFirstName = view.findViewById(R.id.etSupContactFirstName);
|
||||||
|
etSupContactLastName = view.findViewById(R.id.etSupContactLastName);
|
||||||
|
etSupEmail = view.findViewById(R.id.etSupEmail);
|
||||||
|
etSupPhone = view.findViewById(R.id.etSupPhone);
|
||||||
|
btnSaveSupplier = view.findViewById(R.id.btnSaveSupplier);
|
||||||
|
btnDeleteSupplier = view.findViewById(R.id.btnDeleteSupplier);
|
||||||
|
btnBack = view.findViewById(R.id.btnBack);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
package com.example.petstoremobile.fragments.listfragments.listprofilefragments;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.core.content.FileProvider;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.example.petstoremobile.R;
|
||||||
|
import com.example.petstoremobile.fragments.ListFragment;
|
||||||
|
import com.example.petstoremobile.fragments.listfragments.detailfragments.PetDetailFragment;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class PetProfileFragment extends Fragment {
|
||||||
|
|
||||||
|
private TextView tvPetName, tvPetSpecies, tvPetBreed, tvPetAge, tvPetPrice;
|
||||||
|
private Button btnBack, btnEditPet, btnChangePhoto;
|
||||||
|
private ImageView imgPet;
|
||||||
|
private Uri photoUri;
|
||||||
|
|
||||||
|
// launchers for camera and gallery
|
||||||
|
private ActivityResultLauncher<Intent> galleryLauncher;
|
||||||
|
private ActivityResultLauncher<Uri> cameraLauncher;
|
||||||
|
private ActivityResultLauncher<String> permissionLauncher;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
// Launcher to open gallery to select image
|
||||||
|
galleryLauncher = registerForActivityResult(
|
||||||
|
new ActivityResultContracts.StartActivityForResult(),
|
||||||
|
result -> {
|
||||||
|
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
|
||||||
|
Uri selectedImage = result.getData().getData();
|
||||||
|
imgPet.setImageURI(selectedImage);
|
||||||
|
// TODO: SAVE CHANGED PHOTO TO DATABASE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Launcher for camera to open and capture image
|
||||||
|
cameraLauncher = registerForActivityResult(
|
||||||
|
new ActivityResultContracts.TakePicture(),
|
||||||
|
success -> {
|
||||||
|
if (success) {
|
||||||
|
imgPet.setImageURI(null);
|
||||||
|
imgPet.setImageURI(photoUri);
|
||||||
|
// TODO: SAVE CHANGED PHOTO TO DATABASE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Launcher to request camera permission
|
||||||
|
permissionLauncher = registerForActivityResult(
|
||||||
|
new ActivityResultContracts.RequestPermission(),
|
||||||
|
granted -> {
|
||||||
|
if (granted) {
|
||||||
|
launchCamera();
|
||||||
|
} else {
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Permission Required")
|
||||||
|
.setMessage("Please grant camera permission to use this feature")
|
||||||
|
.setPositiveButton("Open Settings", (dialog, which) -> {
|
||||||
|
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||||
|
intent.setData(Uri.fromParts("package", requireContext().getPackageName(), null));
|
||||||
|
startActivity(intent);
|
||||||
|
})
|
||||||
|
.setNegativeButton("Cancel", null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_pet_profile, container, false);
|
||||||
|
|
||||||
|
// Initialize views
|
||||||
|
tvPetName = view.findViewById(R.id.tvPetName);
|
||||||
|
tvPetSpecies = view.findViewById(R.id.tvPetSpecies);
|
||||||
|
tvPetBreed = view.findViewById(R.id.tvPetBreed);
|
||||||
|
tvPetAge = view.findViewById(R.id.tvPetAge);
|
||||||
|
tvPetPrice = view.findViewById(R.id.tvPetPrice);
|
||||||
|
btnBack = view.findViewById(R.id.btnBack);
|
||||||
|
btnEditPet = view.findViewById(R.id.btnEditPet);
|
||||||
|
btnChangePhoto = view.findViewById(R.id.btnChangePhoto);
|
||||||
|
imgPet = view.findViewById(R.id.imgPet);
|
||||||
|
|
||||||
|
|
||||||
|
// Set pet details to display
|
||||||
|
if (getArguments() != null) {
|
||||||
|
tvPetName.setText(getArguments().getString("petName"));
|
||||||
|
tvPetSpecies.setText(getArguments().getString("petSpecies"));
|
||||||
|
tvPetBreed.setText(getArguments().getString("petBreed"));
|
||||||
|
tvPetAge.setText(String.format(Locale.getDefault(), "%d yr(s)", getArguments().getInt("petAge")));
|
||||||
|
tvPetPrice.setText(String.format(Locale.getDefault(), "$%.2f", getArguments().getDouble("petPrice")));
|
||||||
|
}
|
||||||
|
|
||||||
|
//set button click listeners
|
||||||
|
btnBack.setOnClickListener(v -> {
|
||||||
|
//get the list fragment and pop the back stack to return to the previous view (PetFragment)
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.getChildFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Make the edit button go to the pet detail view
|
||||||
|
btnEditPet.setOnClickListener(v -> {
|
||||||
|
if (getArguments() == null) return;
|
||||||
|
|
||||||
|
PetDetailFragment detailFragment = new PetDetailFragment();
|
||||||
|
//send the bundle to the pet detail fragment
|
||||||
|
detailFragment.setArguments(getArguments());
|
||||||
|
|
||||||
|
//get ListFragment to load the the detail view
|
||||||
|
ListFragment listFragment = (ListFragment) getParentFragment();
|
||||||
|
if (listFragment != null) {
|
||||||
|
listFragment.loadFragment(detailFragment);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Make change photo button ask user to select a new photo
|
||||||
|
btnChangePhoto.setOnClickListener(v -> {
|
||||||
|
new AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("Change Pet Photo")
|
||||||
|
.setItems(new String[]{"Take Photo", "Choose from Gallery"}, (dialog, which) -> {
|
||||||
|
if (which == 0) {
|
||||||
|
// Choose Camera
|
||||||
|
//Checks if the user has granted the camera permission already
|
||||||
|
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
//if the permission is already granted then launch the camera
|
||||||
|
launchCamera();
|
||||||
|
} else {
|
||||||
|
//otherwise request the permission
|
||||||
|
permissionLauncher.launch(Manifest.permission.CAMERA);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||||
|
galleryLauncher.launch(intent);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void launchCamera() {
|
||||||
|
File photoFile = new File(requireContext().getCacheDir(), "pet_photo.jpg");
|
||||||
|
photoUri = FileProvider.getUriForFile(requireContext(), requireContext().getPackageName() + ".fileprovider", photoFile);
|
||||||
|
cameraLauncher.launch(photoUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.example.petstoremobile.models;
|
||||||
|
|
||||||
|
public class Adoption {
|
||||||
|
private int adoptionId;
|
||||||
|
private String adopterName;
|
||||||
|
private String adopterEmail;
|
||||||
|
private String adopterPhone;
|
||||||
|
private String petName;
|
||||||
|
private String adoptionDate;
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public Adoption(int adoptionId, String adopterName, String adopterEmail, String adopterPhone, String petName, String adoptionDate, String status) {
|
||||||
|
this.adoptionId = adoptionId;
|
||||||
|
this.adopterName = adopterName;
|
||||||
|
this.adopterEmail = adopterEmail;
|
||||||
|
this.adopterPhone = adopterPhone;
|
||||||
|
this.petName = petName;
|
||||||
|
this.adoptionDate = adoptionDate;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and setters
|
||||||
|
public int getAdoptionId() {
|
||||||
|
return adoptionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdoptionId(int adoptionId) {
|
||||||
|
this.adoptionId = adoptionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAdopterName() {
|
||||||
|
return adopterName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdopterName(String adopterName) {
|
||||||
|
this.adopterName = adopterName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAdopterEmail() {
|
||||||
|
return adopterEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdopterEmail(String adopterEmail) {
|
||||||
|
this.adopterEmail = adopterEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAdopterPhone() {
|
||||||
|
return adopterPhone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdopterPhone(String adopterPhone) {
|
||||||
|
this.adopterPhone = adopterPhone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPetName() {
|
||||||
|
return petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPetName(String petName) {
|
||||||
|
this.petName = petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAdoptionDate() {
|
||||||
|
return adoptionDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAdoptionDate(String adoptionDate) {
|
||||||
|
this.adoptionDate = adoptionDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package com.example.petstoremobile.models;
|
||||||
|
|
||||||
|
|
||||||
|
public class Appointment {
|
||||||
|
private int appointmentId;
|
||||||
|
private String customerName;
|
||||||
|
private String petName;
|
||||||
|
private String serviceType;
|
||||||
|
private String appointmentDate;
|
||||||
|
private String appointmentTime;
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public Appointment(int appointmentId, String customerName, String petName, String serviceType, String appointmentDate, String appointmentTime, String status) {
|
||||||
|
this.appointmentId = appointmentId;
|
||||||
|
this.customerName = customerName;
|
||||||
|
this.petName = petName;
|
||||||
|
this.serviceType = serviceType;
|
||||||
|
this.appointmentDate = appointmentDate;
|
||||||
|
this.appointmentTime = appointmentTime;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and setters
|
||||||
|
public int getAppointmentId() {
|
||||||
|
return appointmentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCustomerName() {
|
||||||
|
return customerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomerName(String customerName) {
|
||||||
|
this.customerName = customerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPetName() {
|
||||||
|
return petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPetName(String petName) {
|
||||||
|
this.petName = petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServiceType() {
|
||||||
|
return serviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServiceType(String serviceType) {
|
||||||
|
this.serviceType = serviceType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppointmentDate() {
|
||||||
|
return appointmentDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppointmentDate(String appointmentDate) {
|
||||||
|
this.appointmentDate = appointmentDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppointmentTime() {
|
||||||
|
return appointmentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppointmentTime(String appointmentTime) {
|
||||||
|
this.appointmentTime = appointmentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(String status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.example.petstoremobile.models;
|
||||||
|
|
||||||
|
public class Chat {
|
||||||
|
private String chatId;
|
||||||
|
private String customerName;
|
||||||
|
private String lastMessage;
|
||||||
|
private Long customerId;
|
||||||
|
private Long staffId;
|
||||||
|
|
||||||
|
public Chat(String chatId, String customerName, String lastMessage, Long customerId, Long staffId) {
|
||||||
|
this.chatId = chatId;
|
||||||
|
this.customerName = customerName;
|
||||||
|
this.lastMessage = lastMessage;
|
||||||
|
this.customerId = customerId;
|
||||||
|
this.staffId = staffId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getChatId() {
|
||||||
|
return chatId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCustomerName() {
|
||||||
|
return customerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLastMessage() {
|
||||||
|
return lastMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCustomerId() {
|
||||||
|
return customerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getStaffId() {
|
||||||
|
return staffId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package com.example.petstoremobile.models;
|
||||||
|
|
||||||
|
|
||||||
|
public class Inventory {
|
||||||
|
private int inventoryId;
|
||||||
|
private String itemName;
|
||||||
|
private String category;
|
||||||
|
private int quantity;
|
||||||
|
private double unitPrice;
|
||||||
|
private String supplier;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public Inventory(int inventoryId, String itemName, String category, int quantity, double unitPrice, String supplier) {
|
||||||
|
this.inventoryId = inventoryId;
|
||||||
|
this.itemName = itemName;
|
||||||
|
this.category = category;
|
||||||
|
this.quantity = quantity;
|
||||||
|
this.unitPrice = unitPrice;
|
||||||
|
this.supplier = supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and setters
|
||||||
|
public int getInventoryId() {
|
||||||
|
return inventoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getItemName() {
|
||||||
|
return itemName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItemName(String itemName) {
|
||||||
|
this.itemName = itemName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCategory() {
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategory(String category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getQuantity() {
|
||||||
|
return quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuantity(int quantity) {
|
||||||
|
this.quantity = quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getUnitPrice() {
|
||||||
|
return unitPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnitPrice(double unitPrice) {
|
||||||
|
this.unitPrice = unitPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSupplier() {
|
||||||
|
return supplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupplier(String supplier) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.example.petstoremobile.models;
|
||||||
|
|
||||||
|
public class Message {
|
||||||
|
private Long id;
|
||||||
|
private Long conversationId;
|
||||||
|
private Long senderId;
|
||||||
|
private String content;
|
||||||
|
private String timestamp;
|
||||||
|
private Boolean isRead;
|
||||||
|
|
||||||
|
public Message() {}
|
||||||
|
|
||||||
|
public Message(Long conversationId, Long senderId, String content) {
|
||||||
|
this.conversationId = conversationId;
|
||||||
|
this.senderId = senderId;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getId() { return id; }
|
||||||
|
public void setId(Long id) { this.id = id; }
|
||||||
|
|
||||||
|
public Long getConversationId() { return conversationId; }
|
||||||
|
public void setConversationId(Long conversationId) { this.conversationId = conversationId; }
|
||||||
|
|
||||||
|
public Long getSenderId() { return senderId; }
|
||||||
|
public void setSenderId(Long senderId) { this.senderId = senderId; }
|
||||||
|
|
||||||
|
public String getContent() { return content; }
|
||||||
|
public void setContent(String content) { this.content = content; }
|
||||||
|
|
||||||
|
public String getTimestamp() { return timestamp; }
|
||||||
|
public void setTimestamp(String timestamp) { this.timestamp = timestamp; }
|
||||||
|
|
||||||
|
public Boolean getIsRead() { return isRead; }
|
||||||
|
public void setIsRead(Boolean isRead) { this.isRead = isRead; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package com.example.petstoremobile.models;
|
||||||
|
|
||||||
|
|
||||||
|
public class Product {
|
||||||
|
private int productId;
|
||||||
|
private String productName;
|
||||||
|
private String productDesc;
|
||||||
|
private String category;
|
||||||
|
private double productPrice;
|
||||||
|
private int stockQuantity;
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
public Product(int productId, String productName, String productDesc, String category, double productPrice, int stockQuantity) {
|
||||||
|
this.productId = productId;
|
||||||
|
this.productName = productName;
|
||||||
|
this.productDesc = productDesc;
|
||||||
|
this.category = category;
|
||||||
|
this.productPrice = productPrice;
|
||||||
|
this.stockQuantity = stockQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters and setters
|
||||||
|
public int getProductId() {
|
||||||
|
return productId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProductName() {
|
||||||
|
return productName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProductName(String productName) {
|
||||||
|
this.productName = productName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProductDesc() {
|
||||||
|
return productDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProductDesc(String productDesc) {
|
||||||
|
this.productDesc = productDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCategory() {
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategory(String category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getProductPrice() {
|
||||||
|
return productPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProductPrice(double productPrice) {
|
||||||
|
this.productPrice = productPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStockQuantity() {
|
||||||
|
return stockQuantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStockQuantity(int stockQuantity) {
|
||||||
|
this.stockQuantity = stockQuantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.example.petstoremobile.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class ActivityLogger {
|
||||||
|
|
||||||
|
private static final String LOG_FILE = "log.txt";
|
||||||
|
|
||||||
|
// Logs a general message with a timestamp
|
||||||
|
public static void log(Context context, String message) {
|
||||||
|
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
|
||||||
|
String entry = timestamp + " | INFO | " + message + "\n";
|
||||||
|
writeToFile(context, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logs a database change (ADD, UPDATE, DELETE) with entity type and ID
|
||||||
|
public static void logChange(Context context, String entity, String action, int id) {
|
||||||
|
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
|
||||||
|
String entry = timestamp + " | DB CHANGE | " + action + " " + entity + " ID: " + id + "\n";
|
||||||
|
writeToFile(context, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logs an exception with location info
|
||||||
|
public static void logException(Context context, String location, Exception e) {
|
||||||
|
String timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date());
|
||||||
|
String entry = timestamp + " | ERROR | " + location + ": " + e.getMessage() + "\n";
|
||||||
|
writeToFile(context, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes the log entry to log.txt in internal storage
|
||||||
|
private static void writeToFile(Context context, String entry) {
|
||||||
|
try {
|
||||||
|
FileWriter fw = new FileWriter(context.getFilesDir() + "/" + LOG_FILE, true);
|
||||||
|
fw.write(entry);
|
||||||
|
fw.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package com.example.petstoremobile.utils;
|
||||||
|
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
public class InputValidator {
|
||||||
|
|
||||||
|
// Checks if an EditText field is not empty
|
||||||
|
public static boolean isNotEmpty(EditText field, String fieldName) {
|
||||||
|
if (field.getText().toString().trim().isEmpty()) {
|
||||||
|
field.setError(fieldName + " is required");
|
||||||
|
field.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the value is a positive integer
|
||||||
|
public static boolean isPositiveInteger(EditText field, String fieldName) {
|
||||||
|
String value = field.getText().toString().trim();
|
||||||
|
try {
|
||||||
|
int num = Integer.parseInt(value);
|
||||||
|
if (num < 0) {
|
||||||
|
field.setError(fieldName + " must be a positive number");
|
||||||
|
field.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
field.setError(fieldName + " must be a whole 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();
|
||||||
|
try {
|
||||||
|
double num = Double.parseDouble(value);
|
||||||
|
if (num < 0) {
|
||||||
|
field.setError(fieldName + " must be a positive number");
|
||||||
|
field.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
field.setError(fieldName + " must be a number");
|
||||||
|
field.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the email address is valid
|
||||||
|
public static boolean isValidEmail(EditText field) {
|
||||||
|
String email = field.getText().toString().trim();
|
||||||
|
if (email.isEmpty() || !android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
|
||||||
|
field.setError("Enter a valid email address");
|
||||||
|
field.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the phone number is valid (digits, spaces, dashes, brackets allowed)
|
||||||
|
public static boolean isValidPhone(EditText field) {
|
||||||
|
String phone = field.getText().toString().trim();
|
||||||
|
if (phone.isEmpty() || !phone.matches("[0-9\\-\\s\\(\\)\\+]+")) {
|
||||||
|
field.setError("Enter a valid phone number");
|
||||||
|
field.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the date is in YYYY-MM-DD format
|
||||||
|
public static boolean isValidDate(EditText field) {
|
||||||
|
String date = field.getText().toString().trim();
|
||||||
|
if (date.isEmpty() || !date.matches("\\d{4}-\\d{2}-\\d{2}")) {
|
||||||
|
field.setError("Date must be in YYYY-MM-DD format");
|
||||||
|
field.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the time format is valid (e.g. 10:00 AM)
|
||||||
|
public static boolean isValidTime(EditText field) {
|
||||||
|
String time = field.getText().toString().trim();
|
||||||
|
if (time.isEmpty() || !time.matches("\\d{1,2}:\\d{2}\\s?(AM|PM|am|pm)?")) {
|
||||||
|
field.setError("Enter a valid time (e.g. 10:00 AM)");
|
||||||
|
field.requestFocus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,295 @@
|
|||||||
|
package com.example.petstoremobile.websocket;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Log;
|
||||||
|
import com.example.petstoremobile.api.RetrofitClient;
|
||||||
|
import com.example.petstoremobile.dtos.ConversationDTO;
|
||||||
|
import com.example.petstoremobile.dtos.MessageDTO;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import ua.naiksoftware.stomp.Stomp;
|
||||||
|
import ua.naiksoftware.stomp.StompClient;
|
||||||
|
import ua.naiksoftware.stomp.dto.StompHeader;
|
||||||
|
|
||||||
|
//Used to handle the websocket connection for the chat
|
||||||
|
public class StompChatManager {
|
||||||
|
|
||||||
|
private static final String TAG = "StompChatManager";
|
||||||
|
|
||||||
|
//Interface for when a message is received
|
||||||
|
public interface MessageListener {
|
||||||
|
void onMessageReceived(MessageDTO message);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Interface for when a conversation is created or updated
|
||||||
|
public interface ConversationListener {
|
||||||
|
void onConversationUpdated(ConversationDTO conversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Interface for when the websocket connection is opened, closed, or has an error
|
||||||
|
public interface ConnectionListener {
|
||||||
|
void onSocketOpened();
|
||||||
|
void onSocketClosed();
|
||||||
|
void onSocketError();
|
||||||
|
}
|
||||||
|
|
||||||
|
private StompClient stompClient;
|
||||||
|
private final CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||||
|
private Disposable topicDisposable;
|
||||||
|
private Disposable conversationsDisposable;
|
||||||
|
private Disposable userConversationsDisposable;
|
||||||
|
private Disposable errorQueueDisposable;
|
||||||
|
private final Gson gson = new Gson();
|
||||||
|
private final Handler reconnectHandler = new Handler(Looper.getMainLooper());
|
||||||
|
private MessageListener messageListener;
|
||||||
|
private ConversationListener conversationListener;
|
||||||
|
private ConnectionListener connectionListener;
|
||||||
|
private final String authToken;
|
||||||
|
private final String role;
|
||||||
|
private boolean isConnected;
|
||||||
|
private boolean isConnecting;
|
||||||
|
private boolean manualDisconnect;
|
||||||
|
private Long pendingConversationId;
|
||||||
|
|
||||||
|
public StompChatManager(String authToken, String role) {
|
||||||
|
this.authToken = authToken;
|
||||||
|
this.role = role == null ? "" : role.trim().toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessageListener(MessageListener listener) {
|
||||||
|
this.messageListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConversationListener(ConversationListener listener) {
|
||||||
|
this.conversationListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConnectionListener(ConnectionListener listener) {
|
||||||
|
this.connectionListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a stomp connection
|
||||||
|
public void connect() {
|
||||||
|
if (authToken == null || authToken.isBlank()) {
|
||||||
|
Log.e(TAG, "Cannot connect websocket without token");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isConnected || isConnecting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
manualDisconnect = false;
|
||||||
|
isConnecting = true;
|
||||||
|
reconnectHandler.removeCallbacksAndMessages(null);
|
||||||
|
String webSocketUrl = buildWebSocketUrl();
|
||||||
|
|
||||||
|
Map<String, String> headers = new HashMap<>();
|
||||||
|
headers.put("Authorization", "Bearer " + authToken);
|
||||||
|
|
||||||
|
stompClient = Stomp.over(Stomp.ConnectionProvider.OKHTTP, webSocketUrl, headers);
|
||||||
|
|
||||||
|
compositeDisposable.add(
|
||||||
|
stompClient.lifecycle()
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(event -> {
|
||||||
|
switch (event.getType()) {
|
||||||
|
case OPENED:
|
||||||
|
isConnected = true;
|
||||||
|
isConnecting = false;
|
||||||
|
Log.d(TAG, "WebSocket opened");
|
||||||
|
if (connectionListener != null) {
|
||||||
|
connectionListener.onSocketOpened();
|
||||||
|
}
|
||||||
|
subscribeToErrorQueue();
|
||||||
|
subscribeToConversationFeeds();
|
||||||
|
if (pendingConversationId != null) {
|
||||||
|
subscribeToTopic(pendingConversationId);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CLOSED:
|
||||||
|
isConnected = false;
|
||||||
|
isConnecting = false;
|
||||||
|
Log.d(TAG, "WebSocket closed");
|
||||||
|
if (connectionListener != null) {
|
||||||
|
connectionListener.onSocketClosed();
|
||||||
|
}
|
||||||
|
scheduleReconnect();
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
isConnected = false;
|
||||||
|
isConnecting = false;
|
||||||
|
Log.e(TAG, "WebSocket error: " + event.getException());
|
||||||
|
if (connectionListener != null) {
|
||||||
|
connectionListener.onSocketError();
|
||||||
|
}
|
||||||
|
scheduleReconnect();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
stompClient.connect(Collections.singletonList(
|
||||||
|
new StompHeader("Authorization", "Bearer " + authToken)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscribes to updates for a specific conversation
|
||||||
|
public void subscribeToConversation(Long conversationId) {
|
||||||
|
pendingConversationId = conversationId;
|
||||||
|
if (!isConnected || stompClient == null) {
|
||||||
|
Log.d(TAG, "Delaying subscription until socket opens for conversation " + conversationId);
|
||||||
|
connect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
subscribeToTopic(conversationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clears the current conversation subscription
|
||||||
|
public void clearConversationSubscription() {
|
||||||
|
pendingConversationId = null;
|
||||||
|
if (topicDisposable != null && !topicDisposable.isDisposed()) {
|
||||||
|
topicDisposable.dispose();
|
||||||
|
topicDisposable = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//helper function to subscribe to a specific conversation topic
|
||||||
|
private void subscribeToTopic(Long conversationId) {
|
||||||
|
if (topicDisposable != null && !topicDisposable.isDisposed()) {
|
||||||
|
topicDisposable.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
String topic = "/topic/chat/conversations/" + conversationId;
|
||||||
|
Log.d(TAG, "Subscribing to topic " + topic);
|
||||||
|
|
||||||
|
topicDisposable = stompClient.topic(topic)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
stompMessage -> {
|
||||||
|
MessageDTO message = gson.fromJson(
|
||||||
|
stompMessage.getPayload(), MessageDTO.class);
|
||||||
|
if (messageListener != null) {
|
||||||
|
messageListener.onMessageReceived(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
throwable -> Log.e(TAG, "Topic error", throwable)
|
||||||
|
);
|
||||||
|
|
||||||
|
compositeDisposable.add(topicDisposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listens for conversation updates and refresh the chat list
|
||||||
|
private void subscribeToConversationFeeds() {
|
||||||
|
if (conversationsDisposable != null && !conversationsDisposable.isDisposed()) {
|
||||||
|
conversationsDisposable.dispose();
|
||||||
|
}
|
||||||
|
if (userConversationsDisposable != null && !userConversationsDisposable.isDisposed()) {
|
||||||
|
userConversationsDisposable.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
userConversationsDisposable = stompClient.topic("/user/queue/chat/conversations")
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
stompMessage -> {
|
||||||
|
Log.d(TAG, "Queue conversation update: " + stompMessage.getPayload());
|
||||||
|
ConversationDTO conversation = gson.fromJson(
|
||||||
|
stompMessage.getPayload(), ConversationDTO.class);
|
||||||
|
if (conversationListener != null) {
|
||||||
|
conversationListener.onConversationUpdated(conversation);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
throwable -> Log.e(TAG, "Conversation queue error", throwable)
|
||||||
|
);
|
||||||
|
|
||||||
|
compositeDisposable.add(userConversationsDisposable);
|
||||||
|
|
||||||
|
if (isCustomer()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conversationsDisposable = stompClient.topic("/topic/chat/conversations")
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
stompMessage -> {
|
||||||
|
Log.d(TAG, "Broadcast conversation update: " + stompMessage.getPayload());
|
||||||
|
ConversationDTO conversation = gson.fromJson(
|
||||||
|
stompMessage.getPayload(), ConversationDTO.class);
|
||||||
|
if (conversationListener != null) {
|
||||||
|
conversationListener.onConversationUpdated(conversation);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
throwable -> Log.e(TAG, "Conversation topic error", throwable)
|
||||||
|
);
|
||||||
|
|
||||||
|
compositeDisposable.add(conversationsDisposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log any error from stomp
|
||||||
|
private void subscribeToErrorQueue() {
|
||||||
|
if (errorQueueDisposable != null && !errorQueueDisposable.isDisposed()) {
|
||||||
|
errorQueueDisposable.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorQueueDisposable = stompClient.topic("/user/queue/chat/errors")
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(
|
||||||
|
stompMessage -> Log.e(TAG, "WebSocket queue error payload: " + stompMessage.getPayload()),
|
||||||
|
throwable -> Log.e(TAG, "WebSocket error queue subscription failed", throwable)
|
||||||
|
);
|
||||||
|
|
||||||
|
compositeDisposable.add(errorQueueDisposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnects the stomp connection
|
||||||
|
public void disconnect() {
|
||||||
|
manualDisconnect = true;
|
||||||
|
isConnected = false;
|
||||||
|
isConnecting = false;
|
||||||
|
pendingConversationId = null;
|
||||||
|
reconnectHandler.removeCallbacksAndMessages(null);
|
||||||
|
compositeDisposable.clear();
|
||||||
|
if (stompClient != null) {
|
||||||
|
stompClient.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the URL for the websocket connection
|
||||||
|
private String buildWebSocketUrl() {
|
||||||
|
String baseUrl = RetrofitClient.BASE_URL.endsWith("/")
|
||||||
|
? RetrofitClient.BASE_URL.substring(0, RetrofitClient.BASE_URL.length() - 1)
|
||||||
|
: RetrofitClient.BASE_URL;
|
||||||
|
if (baseUrl.startsWith("https://")) {
|
||||||
|
return "wss://" + baseUrl.substring("https://".length()) + "/ws/chat";
|
||||||
|
}
|
||||||
|
if (baseUrl.startsWith("http://")) {
|
||||||
|
return "ws://" + baseUrl.substring("http://".length()) + "/ws/chat";
|
||||||
|
}
|
||||||
|
return baseUrl + "/ws/chat";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to check if the current user is a customer
|
||||||
|
private boolean isCustomer() {
|
||||||
|
return "CUSTOMER".equals(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if connection drops, try to reconnect after 1 second
|
||||||
|
private void scheduleReconnect() {
|
||||||
|
if (manualDisconnect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
reconnectHandler.removeCallbacksAndMessages(null);
|
||||||
|
reconnectHandler.postDelayed(this::connect, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
android/app/src/main/res/color/bottom_nav_colors.xml
Normal file
5
android/app/src/main/res/color/bottom_nav_colors.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="@color/accent_coral" android:state_checked="true"/>
|
||||||
|
<item android:color="@color/text_light"/>
|
||||||
|
</selector>
|
||||||
10
android/app/src/main/res/drawable/baseline_menu_36.xml
Normal file
10
android/app/src/main/res/drawable/baseline_menu_36.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="36dp"
|
||||||
|
android:height="36dp"
|
||||||
|
android:viewportWidth="36"
|
||||||
|
android:viewportHeight="36"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M4,27h28v-3L4,24v3zM4,19h28v-3L4,16v3zM4,8v3h28L32,8L4,8z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#E0E0E0" />
|
||||||
|
<corners
|
||||||
|
android:topLeftRadius="16dp"
|
||||||
|
android:topRightRadius="16dp"
|
||||||
|
android:bottomRightRadius="16dp"
|
||||||
|
android:bottomLeftRadius="2dp" />
|
||||||
|
</shape>
|
||||||
9
android/app/src/main/res/drawable/bg_message_sent.xml
Normal file
9
android/app/src/main/res/drawable/bg_message_sent.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="#E74C3C" />
|
||||||
|
<corners
|
||||||
|
android:topLeftRadius="16dp"
|
||||||
|
android:topRightRadius="16dp"
|
||||||
|
android:bottomRightRadius="2dp"
|
||||||
|
android:bottomLeftRadius="16dp" />
|
||||||
|
</shape>
|
||||||
8
android/app/src/main/res/drawable/circle_image.xml
Normal file
8
android/app/src/main/res/drawable/circle_image.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="oval">
|
||||||
|
<solid android:color="#E0E0E0"/>
|
||||||
|
<stroke
|
||||||
|
android:width="2dp"
|
||||||
|
android:color="#BDBDBD"/>
|
||||||
|
</shape>
|
||||||
170
android/app/src/main/res/drawable/ic_launcher_background.xml
Normal file
170
android/app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path
|
||||||
|
android:fillColor="#3DDC84"
|
||||||
|
android:pathData="M0,0h108v108h-108z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M9,0L9,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,0L19,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,0L29,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,0L39,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,0L49,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,0L59,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,0L69,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,0L79,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M89,0L89,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M99,0L99,108"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,9L108,9"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,19L108,19"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,29L108,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,39L108,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,49L108,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,59L108,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,69L108,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,79L108,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,89L108,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M0,99L108,99"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,29L89,29"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,39L89,39"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,49L89,49"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,59L89,59"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,69L89,69"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M19,79L89,79"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29,19L29,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M39,19L39,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M49,19L49,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M59,19L59,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M69,19L69,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M79,19L79,89"
|
||||||
|
android:strokeWidth="0.8"
|
||||||
|
android:strokeColor="#33FFFFFF" />
|
||||||
|
</vector>
|
||||||
30
android/app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
30
android/app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
android:viewportHeight="108">
|
||||||
|
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="85.84757"
|
||||||
|
android:endY="92.4963"
|
||||||
|
android:startX="42.9492"
|
||||||
|
android:startY="49.59793"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0" />
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0" />
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeColor="#00000000" />
|
||||||
|
</vector>
|
||||||
BIN
android/app/src/main/res/drawable/petstore_logo.png
Normal file
BIN
android/app/src/main/res/drawable/petstore_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
BIN
android/app/src/main/res/drawable/placeholder.png
Normal file
BIN
android/app/src/main/res/drawable/placeholder.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 212 KiB |
9
android/app/src/main/res/drawable/rounded_card.xml
Normal file
9
android/app/src/main/res/drawable/rounded_card.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#FFFFFF"/>
|
||||||
|
<corners android:radius="12dp"/>
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="#EEEEEE"/>
|
||||||
|
</shape>
|
||||||
25
android/app/src/main/res/layout/activity_home.xml
Normal file
25
android/app/src/main/res/layout/activity_home.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
android:id="@+id/bottom_navigation"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/primary_dark"
|
||||||
|
app:itemIconTint="@color/bottom_nav_colors"
|
||||||
|
app:itemTextColor="@color/bottom_nav_colors"
|
||||||
|
app:menu="@menu/bottom_nav_menu"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
113
android/app/src/main/res/layout/activity_main.xml
Normal file
113
android/app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/primary_dark">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="172dp"
|
||||||
|
android:layout_height="170dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/petstore_logo" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Leon's Petstore"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="28sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Staff Portal"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="14sp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvLoginStatus"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:text="login status"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textColor="#DB4848"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_weight="0.2"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:padding="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Sign In"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="24dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Username"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etUser"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter username"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Password"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPassword"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter password"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnLogin"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Sign In"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
80
android/app/src/main/res/layout/fragment_adoption.xml
Normal file
80
android/app/src/main/res/layout/fragment_adoption.xml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?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/header"
|
||||||
|
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/btnHamburger"
|
||||||
|
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="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Adoptions"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearchAdoption"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:hint="Search by adopter or pet name..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:drawableStart="@android:drawable/ic_menu_search"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/swipeRefreshAdoption"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerViewAdoptions"
|
||||||
|
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/fabAddAdoption"
|
||||||
|
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 Adoption"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
app:tint="@color/white"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
201
android/app/src/main/res/layout/fragment_adoption_detail.xml
Normal file
201
android/app/src/main/res/layout/fragment_adoption_detail.xml
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<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/tvAdoptionMode"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Add Adoption"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnDeleteAdoption"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:text="Delete"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAdoptionId"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="ID: #0"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Adopter Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etAdopterName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter adopter name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Adopter Email"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etAdopterEmail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter email address"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Adopter Phone"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etAdopterPhone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter phone number"
|
||||||
|
android:inputType="phone"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Pet Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etAdoptionPetName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter pet name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Adoption Date"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etAdoptionDate"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="YYYY-MM-DD"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Status"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinnerAdoptionStatus"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<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/btnAdoptionBack"
|
||||||
|
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/btnSaveAdoption"
|
||||||
|
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>
|
||||||
80
android/app/src/main/res/layout/fragment_appointment.xml
Normal file
80
android/app/src/main/res/layout/fragment_appointment.xml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?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/header"
|
||||||
|
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/btnHamburger"
|
||||||
|
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="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Appointments"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearchAppointment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:hint="Search by customer, pet or service..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:drawableStart="@android:drawable/ic_menu_search"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/swipeRefreshAppointment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerViewAppointments"
|
||||||
|
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/fabAddAppointment"
|
||||||
|
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 Appointment"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
app:tint="@color/white"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
197
android/app/src/main/res/layout/fragment_appointment_detail.xml
Normal file
197
android/app/src/main/res/layout/fragment_appointment_detail.xml
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<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/tvApptMode"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Add Appointment"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnDeleteAppointment"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:text="Delete"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAppointmentId"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="ID: #0"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Customer Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etCustomerName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter customer name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Pet Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etApptPetName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter pet name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Service Type"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etServiceType"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="e.g. Grooming, Vet Checkup"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Appointment Date"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etAppointmentDate"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="YYYY-MM-DD"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Appointment Time"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etAppointmentTime"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="e.g. 10:00 AM"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Status"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinnerAppointmentStatus"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<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/btnApptBack"
|
||||||
|
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/btnSaveAppointment"
|
||||||
|
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>
|
||||||
102
android/app/src/main/res/layout/fragment_chat.xml
Normal file
102
android/app/src/main/res/layout/fragment_chat.xml
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/chatDrawerLayout"
|
||||||
|
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">
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnHamburger"
|
||||||
|
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="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Customer Chat"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvMessages"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:clipToPadding="false" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="8dp"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etMessage"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:hint="Type a message..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnSend"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Send"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:textColor="@color/white"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/chatListDrawer"
|
||||||
|
android:layout_width="260dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/primary_dark"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Active Chats"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvChatList"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.drawerlayout.widget.DrawerLayout>
|
||||||
80
android/app/src/main/res/layout/fragment_inventory.xml
Normal file
80
android/app/src/main/res/layout/fragment_inventory.xml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?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/header"
|
||||||
|
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/btnHamburger"
|
||||||
|
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="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Inventory"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearchInventory"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:hint="Search by item name or category..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:drawableStart="@android:drawable/ic_menu_search"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/swipeRefreshInventory"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerViewInventory"
|
||||||
|
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/fabAddInventory"
|
||||||
|
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 Inventory Item"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
app:tint="@color/white"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
182
android/app/src/main/res/layout/fragment_inventory_detail.xml
Normal file
182
android/app/src/main/res/layout/fragment_inventory_detail.xml
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<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/tvInventoryMode"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Add Inventory Item"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnDeleteInventory"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:text="Delete"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvInventoryId"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="ID: #0"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Item Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etItemName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter item name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Category"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etInventoryCategory"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="e.g. Food, Toys, Medicine"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Quantity"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etQuantity"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter quantity"
|
||||||
|
android:inputType="number"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Unit Price"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etUnitPrice"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter unit price"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Supplier"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etInventorySupplier"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter supplier name"
|
||||||
|
android:inputType="text"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<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/btnInventoryBack"
|
||||||
|
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/btnSaveInventory"
|
||||||
|
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>
|
||||||
214
android/app/src/main/res/layout/fragment_list.xml
Normal file
214
android/app/src/main/res/layout/fragment_list.xml
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.drawerlayout.widget.DrawerLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/drawerLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/inner_fragment_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/touchBlocker"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clickable="false"
|
||||||
|
android:focusable="false"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/drawer"
|
||||||
|
android:layout_width="260dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="start"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/primary_dark">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/primary_medium"
|
||||||
|
android:padding="24dp"
|
||||||
|
android:paddingTop="48dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="60dp"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:src="@drawable/petstore_logo"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:layout_marginBottom="12dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Leon's Petstore"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Staff Portal"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="13sp"/>
|
||||||
|
|
||||||
|
</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"
|
||||||
|
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/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/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/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/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/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/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>
|
||||||
|
|
||||||
|
</androidx.drawerlayout.widget.DrawerLayout>
|
||||||
80
android/app/src/main/res/layout/fragment_pet.xml
Normal file
80
android/app/src/main/res/layout/fragment_pet.xml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?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/header"
|
||||||
|
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/btnHamburger"
|
||||||
|
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="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Pets"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearchPet"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:hint="Search by name, species or breed..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:drawableStart="@android:drawable/ic_menu_search"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/swipeRefreshPet"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerViewPets"
|
||||||
|
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/fabAddPet"
|
||||||
|
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 Pet"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
app:tint="@color/white"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
201
android/app/src/main/res/layout/fragment_pet_detail.xml
Normal file
201
android/app/src/main/res/layout/fragment_pet_detail.xml
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<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/tvMode"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Add Pet"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnDeletePet"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:text="Delete"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetId"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Pet ID: #0"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Pet Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPetName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter pet name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Species"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPetSpecies"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="e.g. Dog, Cat, Bird"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Breed"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPetBreed"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter breed"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Age"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPetAge"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter age"
|
||||||
|
android:inputType="number"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Price"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etPetPrice"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter price"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Status"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/spinnerPetStatus"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<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/btnBack"
|
||||||
|
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/btnSavePet"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:text="Save"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
244
android/app/src/main/res/layout/fragment_pet_profile.xml
Normal file
244
android/app/src/main/res/layout/fragment_pet_profile.xml
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/primary_dark">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Pet Profile"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnEditPet"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Edit"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:textColor="@color/white"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imgPet"
|
||||||
|
android:layout_width="146dp"
|
||||||
|
android:layout_height="140dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:background="@drawable/circle_image"
|
||||||
|
android:clipToOutline="true"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/placeholder" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnChangePhoto"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Change Photo"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="NAME"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="26sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</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="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:layout_marginEnd="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="SPECIES"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetSpecies"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Dog"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:layout_marginStart="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="BREED"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetBreed"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Labrador"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_marginTop="8dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:layout_marginEnd="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="AGE"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetAge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="2 yr(s)"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:gravity="center"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:layout_marginStart="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="PRICE"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetPrice"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="$500.00"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:textColor="@color/accent_coral"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnBack"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Back"
|
||||||
|
android:backgroundTint="@color/primary_medium"
|
||||||
|
android:textColor="@color/white"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
80
android/app/src/main/res/layout/fragment_product.xml
Normal file
80
android/app/src/main/res/layout/fragment_product.xml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?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/header"
|
||||||
|
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/btnHamburger"
|
||||||
|
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="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Products"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearchProduct"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:hint="Search by product name or category..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:drawableStart="@android:drawable/ic_menu_search"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/swipeRefreshProduct"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerViewProducts"
|
||||||
|
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/fabAddProduct"
|
||||||
|
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 Product"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
app:tint="@color/white"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
183
android/app/src/main/res/layout/fragment_product_detail.xml
Normal file
183
android/app/src/main/res/layout/fragment_product_detail.xml
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<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/tvProductMode"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Add Product"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnDeleteProduct"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:text="Delete"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvProductId"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="ID: #0"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Product Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etProductName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter product name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Description"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etProductDesc"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Enter product description"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:minLines="1" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Category"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etProductCategory"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="e.g. Food, Toys, Grooming"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Price"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etProductPrice"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter price"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Stock Quantity"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etStockQuantity"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter stock quantity"
|
||||||
|
android:inputType="number"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<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/btnProductBack"
|
||||||
|
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/btnSaveProduct"
|
||||||
|
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>
|
||||||
200
android/app/src/main/res/layout/fragment_profile.xml
Normal file
200
android/app/src/main/res/layout/fragment_profile.xml
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/primary_dark"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
|
android:paddingTop="32dp"
|
||||||
|
android:paddingBottom="32dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imgProfile"
|
||||||
|
android:layout_width="171dp"
|
||||||
|
android:layout_height="166dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:background="@drawable/circle_image"
|
||||||
|
android:clipToOutline="true"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/placeholder" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnChangePhoto"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Change Photo"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:textColor="@color/text_light"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvProfileName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="First Last"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingBottom="12dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Email"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="#888888"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvProfileEmail"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="example@email.com"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnEditEmail"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Edit"
|
||||||
|
android:textColor="@color/accent_coral"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="#F0F0F0"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="12dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Phone"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="#888888"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvProfilePhone"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="(123) 123-1234"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnEditPhone"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Edit"
|
||||||
|
android:textColor="@color/accent_coral"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="#F0F0F0"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingTop="12dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Role"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="#888888"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvProfileRole"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Manager"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textColor="@color/accent_coral"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnLogout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:text="Log Out"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:textColor="@color/white"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
80
android/app/src/main/res/layout/fragment_service.xml
Normal file
80
android/app/src/main/res/layout/fragment_service.xml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?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/header"
|
||||||
|
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/btnHamburger"
|
||||||
|
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="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Services"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearchService"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:hint="Search by service name or description..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:drawableStart="@android:drawable/ic_menu_search"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/swipeRefreshService"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerViewServices"
|
||||||
|
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/fabAddService"
|
||||||
|
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 Service"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
app:tint="@color/white"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
171
android/app/src/main/res/layout/fragment_service_detail.xml
Normal file
171
android/app/src/main/res/layout/fragment_service_detail.xml
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<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/tvMode"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Add Service"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnDeleteService"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:text="Delete"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvServiceId"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="ID: #0"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Service Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etServiceName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter service name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Description"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etServiceDesc"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Enter description"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:minLines="1"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Duration (minutes)"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etServiceDuration"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter duration in minutes"
|
||||||
|
android:inputType="number"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Price"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etServicePrice"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter price"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<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/btnBack"
|
||||||
|
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/btnSaveService"
|
||||||
|
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>
|
||||||
80
android/app/src/main/res/layout/fragment_supplier.xml
Normal file
80
android/app/src/main/res/layout/fragment_supplier.xml
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
<?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/header"
|
||||||
|
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/btnHamburger"
|
||||||
|
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="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Suppliers"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearchSupplier"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:hint="Search by company or contact name..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:drawableStart="@android:drawable/ic_menu_search"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:background="@android:color/white"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:textColor="@color/text_dark"/>
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/swipeRefreshSupplier"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerViewSuppliers"
|
||||||
|
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/fabAddSupplier"
|
||||||
|
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 Supplier"
|
||||||
|
app:srcCompat="@android:drawable/ic_input_add"
|
||||||
|
app:tint="@color/white"/>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
182
android/app/src/main/res/layout/fragment_supplier_detail.xml
Normal file
182
android/app/src/main/res/layout/fragment_supplier_detail.xml
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@color/background_grey">
|
||||||
|
|
||||||
|
<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/tvMode"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Add Supplier"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/btnDeleteSupplier"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:backgroundTint="@color/accent_coral"
|
||||||
|
android:text="Delete"
|
||||||
|
android:textColor="@color/white" />
|
||||||
|
|
||||||
|
</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">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvSupId"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="ID: #0"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="11sp"
|
||||||
|
android:textStyle="italic"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Company Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSupCompany"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter company name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Contact First Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSupContactFirstName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter first name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Contact Last Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSupContactLastName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter last name"
|
||||||
|
android:inputType="text"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Email"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSupEmail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter email"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:layout_marginBottom="16dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Phone"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSupPhone"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="Enter phone number"
|
||||||
|
android:inputType="phone"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
<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/btnBack"
|
||||||
|
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/btnSaveSupplier"
|
||||||
|
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>
|
||||||
64
android/app/src/main/res/layout/item_adoption.xml
Normal file
64
android/app/src/main/res/layout/item_adoption.xml
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?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="@android:color/white">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAdopterName"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Adopter Name"
|
||||||
|
android:textColor="#000000"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAdoptionStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="#4CAF50"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:text="Status"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAdoptionPetName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Pet: name"
|
||||||
|
android:textColor="#666666"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAdoptionDate"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Date: "
|
||||||
|
android:textColor="#666666"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="#EEEEEE"
|
||||||
|
android:layout_marginTop="12dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
74
android/app/src/main/res/layout/item_appointment.xml
Normal file
74
android/app/src/main/res/layout/item_appointment.xml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?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="@android:color/white">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvCustomerName"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Customer Name"
|
||||||
|
android:textColor="#000000"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvAppointmentStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="#4CAF50"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingTop="4dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:text="Status"
|
||||||
|
android:textColor="#FFFFFF"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvApptPetName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Pet: name"
|
||||||
|
android:textColor="#666666"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvServiceType"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:text="Service type"
|
||||||
|
android:textColor="#666666"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDateTime"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Date and time"
|
||||||
|
android:textColor="#2196F3"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="#EEEEEE"
|
||||||
|
android:layout_marginTop="12dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
26
android/app/src/main/res/layout/item_chat.xml
Normal file
26
android/app/src/main/res/layout/item_chat.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?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="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvCustomerName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvLastMessage"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textColor="@color/text_light"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:ellipsize="end" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
81
android/app/src/main/res/layout/item_inventory.xml
Normal file
81
android/app/src/main/res/layout/item_inventory.xml
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<?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:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvItemName"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="Item Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvUnitPrice"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="$0.00"
|
||||||
|
android:textColor="@color/accent_coral"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvCategory"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Category"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginTop="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvQuantity"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Qty: 0"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvInvSupplier"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Supplier: "
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="#F0F0F0"
|
||||||
|
android:layout_marginTop="12dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
19
android/app/src/main/res/layout/item_message_received.xml
Normal file
19
android/app/src/main/res/layout/item_message_received.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?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="8dp"
|
||||||
|
android:gravity="start">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMessageContent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_message_received"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:text="Received message"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:maxWidth="300dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
19
android/app/src/main/res/layout/item_message_sent.xml
Normal file
19
android/app/src/main/res/layout/item_message_sent.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?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="8dp"
|
||||||
|
android:gravity="end">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvMessageContent"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_message_sent"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:text="Sent message"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:maxWidth="300dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
89
android/app/src/main/res/layout/item_pet.xml
Normal file
89
android/app/src/main/res/layout/item_pet.xml
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
<?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:paddingStart="16dp"
|
||||||
|
android:paddingEnd="16dp"
|
||||||
|
android:paddingTop="16dp"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetName"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="Pet Name"
|
||||||
|
android:textColor="@color/text_dark"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="8dp"
|
||||||
|
android:paddingTop="3dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingBottom="3dp"
|
||||||
|
android:text="Status"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="11sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetSpeciesBreed"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:text="Breed"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_marginTop="8dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetPrice"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="$126.00"
|
||||||
|
android:textColor="@color/accent_coral"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvPetAge"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="1"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:textSize="13sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="#F0F0F0"
|
||||||
|
android:layout_marginTop="12dp"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user