..
This commit is contained in:
@@ -5,12 +5,12 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "com.example.pap_teste"
|
||||
compileSdk = 36
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.pap_teste"
|
||||
minSdk = 24
|
||||
targetSdk = 36
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
@@ -44,6 +44,11 @@ dependencies {
|
||||
implementation(libs.firebase.database)
|
||||
implementation(libs.glide)
|
||||
implementation(libs.firebase.storage)
|
||||
|
||||
// Room
|
||||
implementation(libs.room.runtime)
|
||||
annotationProcessor(libs.room.compiler)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
androidTestImplementation(libs.ext.junit)
|
||||
androidTestImplementation(libs.espresso.core)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
|
||||
<activity
|
||||
android:name=".OnboardingActivity"
|
||||
android:name=".SplashActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
@@ -35,6 +35,10 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".OnboardingActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="false" />
|
||||
@@ -50,6 +54,42 @@
|
||||
<activity
|
||||
android:name=".ReservationActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".FavoritosActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".EstablishmentDashboardActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".AccountCreatedActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".NovaReservaActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".CheckInAntecipadoActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".MinhasReservasActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".PartilharReservaActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".ProfileDashboardActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".ExplorarRestaurantesActivity"
|
||||
android:exported="false" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import static android.content.Intent.getIntent;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Button;
|
||||
@@ -50,13 +52,13 @@ public class AccountCreatedActivity extends AppCompatActivity {
|
||||
this,
|
||||
isEstablishment ? EstablishmentDashboardActivity.class : ClientDashboardActivity.class
|
||||
);
|
||||
nextScreen.putExtra(MainActivity.EXTRA_ACTION_MODE, MainActivity.AccountAction.CRIAR.name());
|
||||
// nextScreen.putExtra(MainActivity.EXTRA_ACTION_MODE, MainActivity.AccountAction.CRIAR.name());
|
||||
nextScreen.putExtra(MainActivity.EXTRA_DISPLAY_NAME, displayName);
|
||||
nextScreen.putExtra(MainActivity.EXTRA_EMAIL, email);
|
||||
nextScreen.putExtra(MainActivity.EXTRA_ACCOUNT_TYPE, accountType);
|
||||
nextScreen.putExtra(MainActivity.EXTRA_ROLE, role);
|
||||
startActivity(nextScreen);
|
||||
finish();
|
||||
//startActivity(nextScreen);
|
||||
//finish();
|
||||
});
|
||||
|
||||
if (btnBack != null) {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
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.pap_teste.models.LocalReservation;
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
import com.google.android.material.chip.Chip;
|
||||
import java.util.List;
|
||||
|
||||
public class LocalReservaAdapter extends RecyclerView.Adapter<LocalReservaAdapter.ViewHolder> {
|
||||
|
||||
private final List<LocalReservation> reservations;
|
||||
private final OnActionListener listener;
|
||||
|
||||
public interface OnActionListener {
|
||||
void onCancel(LocalReservation reservation);
|
||||
void onEdit(LocalReservation reservation);
|
||||
}
|
||||
|
||||
public LocalReservaAdapter(List<LocalReservation> reservations, OnActionListener listener) {
|
||||
this.reservations = reservations;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_reserva_local, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
LocalReservation res = reservations.get(position);
|
||||
holder.restaurantName.setText(res.getRestaurantName());
|
||||
holder.dateInfo.setText(res.getDate() + " às " + res.getTime());
|
||||
holder.tableInfo.setText("Mesa " + res.getTableNumber() + " • " + res.getGuests() + " pessoas");
|
||||
holder.statusChip.setText(res.getStatus());
|
||||
|
||||
holder.btnCancel.setOnClickListener(v -> listener.onCancel(res));
|
||||
holder.itemView.setOnClickListener(v -> listener.onEdit(res));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return reservations.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView restaurantName, dateInfo, tableInfo;
|
||||
Chip statusChip;
|
||||
MaterialButton btnCancel;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
restaurantName = itemView.findViewById(R.id.txtRestauranteNome);
|
||||
dateInfo = itemView.findViewById(R.id.txtDataHora);
|
||||
tableInfo = itemView.findViewById(R.id.txtMesaPessoas);
|
||||
statusChip = itemView.findViewById(R.id.chipStatus);
|
||||
btnCancel = itemView.findViewById(R.id.btnCancelar);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
|
||||
// Check if user is already logged in
|
||||
if (mAuth.getCurrentUser() != null) {
|
||||
startActivity(new Intent(this, ClientDashboardActivity.class));
|
||||
android.app.ActivityOptions options = android.app.ActivityOptions.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
startActivity(new Intent(this, ClientDashboardActivity.class), options.toBundle());
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
@@ -101,7 +102,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
User newUser = new User(uid, name, email, "CLIENTE");
|
||||
db.collection("users").document(uid).set(newUser)
|
||||
.addOnSuccessListener(aVoid -> {
|
||||
startActivity(new Intent(this, ClientDashboardActivity.class));
|
||||
android.app.ActivityOptions options = android.app.ActivityOptions.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
startActivity(new Intent(this, ClientDashboardActivity.class), options.toBundle());
|
||||
finish();
|
||||
});
|
||||
})
|
||||
|
||||
@@ -9,23 +9,22 @@ import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.pap_teste.models.Reserva;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.firestore.FirebaseFirestore;
|
||||
import com.google.firebase.firestore.Query;
|
||||
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||
import com.example.pap_teste.models.LocalReservation;
|
||||
import com.example.pap_teste.viewmodels.ReservationViewModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MyReservationsFragment extends Fragment {
|
||||
|
||||
private RecyclerView rvReservations;
|
||||
private ReservaAdapter adapter;
|
||||
private List<Reserva> reservationList = new ArrayList<>();
|
||||
private LocalReservaAdapter adapter;
|
||||
private List<LocalReservation> reservationList = new ArrayList<>();
|
||||
private ProgressBar progressBar;
|
||||
private View emptyState;
|
||||
private FirebaseFirestore db;
|
||||
private ReservationViewModel viewModel;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -36,63 +35,41 @@ public class MyReservationsFragment extends Fragment {
|
||||
progressBar = view.findViewById(R.id.progressBar);
|
||||
emptyState = view.findViewById(R.id.emptyState);
|
||||
|
||||
db = FirebaseFirestore.getInstance();
|
||||
viewModel = new ViewModelProvider(this).get(ReservationViewModel.class);
|
||||
|
||||
setupAdapter();
|
||||
loadReservations();
|
||||
observeReservations();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void setupAdapter() {
|
||||
adapter = new ReservaAdapter(reservationList, new ReservaAdapter.OnReservaActionListener() {
|
||||
adapter = new LocalReservaAdapter(reservationList, new LocalReservaAdapter.OnActionListener() {
|
||||
@Override
|
||||
public void onCheckIn(Reserva reserva) {
|
||||
// Implement check-in logic if needed
|
||||
Toast.makeText(getContext(), "Check-in disponível em breve.", Toast.LENGTH_SHORT).show();
|
||||
public void onCancel(LocalReservation reservation) {
|
||||
reservation.setStatus("CANCELADA");
|
||||
viewModel.update(reservation);
|
||||
Toast.makeText(getContext(), "Reserva cancelada.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel(Reserva reserva) {
|
||||
cancelReservation(reserva);
|
||||
public void onEdit(LocalReservation reservation) {
|
||||
// Show details or edit
|
||||
Toast.makeText(getContext(), "Detalhes: " + reservation.getNotes(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
rvReservations.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
rvReservations.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private void loadReservations() {
|
||||
String userId = FirebaseAuth.getInstance().getUid();
|
||||
if (userId == null) return;
|
||||
|
||||
private void observeReservations() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
db.collection("reservations")
|
||||
.whereEqualTo("userId", userId)
|
||||
.orderBy("date", Query.Direction.DESCENDING)
|
||||
.get()
|
||||
.addOnCompleteListener(task -> {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
if (task.isSuccessful()) {
|
||||
reservationList.clear();
|
||||
for (QueryDocumentSnapshot document : task.getResult()) {
|
||||
Reserva r = document.toObject(Reserva.class);
|
||||
r.setId(document.getId());
|
||||
reservationList.add(r);
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
emptyState.setVisibility(reservationList.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
} else {
|
||||
Toast.makeText(getContext(), "Erro ao carregar reservas.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void cancelReservation(Reserva reserva) {
|
||||
db.collection("reservations").document(reserva.getId())
|
||||
.update("status", "cancelled")
|
||||
.addOnSuccessListener(aVoid -> {
|
||||
Toast.makeText(getContext(), "Reserva cancelada.", Toast.LENGTH_SHORT).show();
|
||||
loadReservations();
|
||||
})
|
||||
.addOnFailureListener(e -> Toast.makeText(getContext(), "Erro ao cancelar.", Toast.LENGTH_SHORT).show());
|
||||
viewModel.getAllReservations().observe(getViewLifecycleOwner(), reservations -> {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
reservationList.clear();
|
||||
reservationList.addAll(reservations);
|
||||
adapter.notifyDataSetChanged();
|
||||
emptyState.setVisibility(reservationList.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,333 +1,261 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
import android.app.TimePickerDialog;
|
||||
import android.os.Bundle;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import android.widget.Button;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.pap_teste.models.FoodCategory;
|
||||
import com.example.pap_teste.models.LocalReservation;
|
||||
import com.example.pap_teste.models.Mesa;
|
||||
import com.example.pap_teste.models.Restaurant;
|
||||
import com.example.pap_teste.viewmodels.ReservationViewModel;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.database.DataSnapshot;
|
||||
import com.google.firebase.database.DatabaseError;
|
||||
import com.google.firebase.database.DatabaseReference;
|
||||
import com.google.firebase.database.FirebaseDatabase;
|
||||
import com.google.firebase.database.ValueEventListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class NovaReservaActivity extends AppCompatActivity {
|
||||
|
||||
private enum State {
|
||||
CATEGORIES, RESTAURANTS, DETAILS
|
||||
}
|
||||
private enum State { CATEGORIES, RESTAURANTS, DETAILS }
|
||||
|
||||
private State currentState = State.CATEGORIES;
|
||||
private String selectedCategory = null;
|
||||
private com.example.pap_teste.models.Restaurant selectedRestaurant = null;
|
||||
private Restaurant selectedRestaurant = null;
|
||||
private Mesa selectedMesa = null;
|
||||
private String selectedDate = null;
|
||||
private String selectedTime = null;
|
||||
|
||||
private androidx.recyclerview.widget.RecyclerView rvCategories, rvRestaurants;
|
||||
private android.view.View scrollNovaReserva;
|
||||
private android.widget.TextView txtTitle;
|
||||
private android.widget.ProgressBar progressBar;
|
||||
private RecyclerView rvCategories, rvRestaurants, rvTables;
|
||||
private View scrollNovaReserva;
|
||||
private TextView txtTitle;
|
||||
private ProgressBar progressBar;
|
||||
private ReservationViewModel viewModel;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
EdgeToEdge.enable(this);
|
||||
setContentView(R.layout.activity_nova_reserva);
|
||||
|
||||
viewModel = new ViewModelProvider(this).get(ReservationViewModel.class);
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.novaReservaRoot), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
|
||||
initViews();
|
||||
setupCategories();
|
||||
updateViewState();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
rvCategories = findViewById(R.id.rvCategories);
|
||||
rvRestaurants = findViewById(R.id.rvRestaurants);
|
||||
rvTables = findViewById(R.id.rvTables);
|
||||
scrollNovaReserva = findViewById(R.id.scrollNovaReserva);
|
||||
txtTitle = findViewById(R.id.txtTituloNovaReserva);
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
|
||||
Button back = findViewById(R.id.btnVoltar);
|
||||
if (back != null) {
|
||||
back.setOnClickListener(v -> handleBackNavigation());
|
||||
}
|
||||
|
||||
setupCategories();
|
||||
updateViewState();
|
||||
findViewById(R.id.btnVoltar).setOnClickListener(v -> handleBackNavigation());
|
||||
findViewById(R.id.btnSelectDate).setOnClickListener(v -> showDatePicker());
|
||||
findViewById(R.id.btnSelectTime).setOnClickListener(v -> showTimePicker());
|
||||
findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> validateAndSave());
|
||||
}
|
||||
|
||||
private void handleBackNavigation() {
|
||||
if (currentState == State.RESTAURANTS) {
|
||||
currentState = State.CATEGORIES;
|
||||
updateViewState();
|
||||
} else if (currentState == State.DETAILS) {
|
||||
currentState = State.RESTAURANTS;
|
||||
updateViewState();
|
||||
} else {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
updateViewState();
|
||||
}
|
||||
|
||||
private void updateViewState() {
|
||||
rvCategories
|
||||
.setVisibility(currentState == State.CATEGORIES ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||
rvRestaurants
|
||||
.setVisibility(currentState == State.RESTAURANTS ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||
scrollNovaReserva
|
||||
.setVisibility(currentState == State.DETAILS ? android.view.View.VISIBLE : android.view.View.GONE);
|
||||
rvCategories.setVisibility(currentState == State.CATEGORIES ? View.VISIBLE : View.GONE);
|
||||
rvRestaurants.setVisibility(currentState == State.RESTAURANTS ? View.VISIBLE : View.GONE);
|
||||
scrollNovaReserva.setVisibility(currentState == State.DETAILS ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (currentState == State.CATEGORIES) {
|
||||
txtTitle.setText("Escolha o tema");
|
||||
} else if (currentState == State.RESTAURANTS) {
|
||||
txtTitle.setText("Restaurantes: " + selectedCategory);
|
||||
} else {
|
||||
txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : ""));
|
||||
setupReservationOptions();
|
||||
switch (currentState) {
|
||||
case CATEGORIES: txtTitle.setText("Escolha o tema"); break;
|
||||
case RESTAURANTS: txtTitle.setText("Restaurantes: " + selectedCategory); break;
|
||||
case DETAILS: txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : "")); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void setupCategories() {
|
||||
java.util.List<com.example.pap_teste.models.FoodCategory> cats = new java.util.ArrayList<>();
|
||||
cats.add(new com.example.pap_teste.models.FoodCategory("Carnes", R.drawable.cat_carnes));
|
||||
cats.add(new com.example.pap_teste.models.FoodCategory("Massas", R.drawable.cat_massas));
|
||||
cats.add(new com.example.pap_teste.models.FoodCategory("Sushi", R.drawable.cat_sushi));
|
||||
cats.add(new com.example.pap_teste.models.FoodCategory("Pizzas", R.drawable.cat_pizzas));
|
||||
cats.add(new com.example.pap_teste.models.FoodCategory("Sobremesas", R.drawable.cat_sobremesas));
|
||||
List<FoodCategory> cats = new ArrayList<>();
|
||||
cats.add(new FoodCategory("Carnes", R.drawable.cat_carnes));
|
||||
cats.add(new FoodCategory("Massas", R.drawable.cat_massas));
|
||||
cats.add(new FoodCategory("Sushi", R.drawable.cat_sushi));
|
||||
cats.add(new FoodCategory("Pizzas", R.drawable.cat_pizzas));
|
||||
cats.add(new FoodCategory("Sobremesas", R.drawable.cat_sobremesas));
|
||||
|
||||
rvCategories.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(this));
|
||||
rvCategories.setLayoutManager(new LinearLayoutManager(this));
|
||||
rvCategories.setAdapter(new FoodCategoryAdapter(cats, category -> {
|
||||
selectedCategory = category.getName();
|
||||
currentState = State.RESTAURANTS;
|
||||
setupRestaurants();
|
||||
loadRestaurants();
|
||||
updateViewState();
|
||||
}));
|
||||
}
|
||||
|
||||
private void setupRestaurants() {
|
||||
java.util.List<com.example.pap_teste.models.Restaurant> filteredList = new java.util.ArrayList<>();
|
||||
com.google.firebase.database.DatabaseReference usersRef = com.google.firebase.database.FirebaseDatabase
|
||||
.getInstance().getReference("Restaurantes");
|
||||
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.VISIBLE);
|
||||
|
||||
usersRef.orderByChild("category").equalTo(selectedCategory)
|
||||
.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
filteredList.clear();
|
||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||
String role = ds.child("role").getValue(String.class);
|
||||
String accountType = ds.child("accountType").getValue(String.class);
|
||||
|
||||
if ("ADMIN".equalsIgnoreCase(role) || "ESTABELECIMENTO".equalsIgnoreCase(accountType)) {
|
||||
String name = ds.child("establishmentName").getValue(String.class);
|
||||
if (name == null)
|
||||
name = ds.child("displayName").getValue(String.class);
|
||||
String email = ds.child("email").getValue(String.class);
|
||||
String cat = ds.child("category").getValue(String.class);
|
||||
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
||||
|
||||
if (name != null && email != null) {
|
||||
filteredList.add(new com.example.pap_teste.models.Restaurant(name, cat, email,
|
||||
false, logoUrl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rvRestaurants.setLayoutManager(
|
||||
new androidx.recyclerview.widget.LinearLayoutManager(NovaReservaActivity.this));
|
||||
rvRestaurants.setAdapter(new RestaurantAdapter(filteredList, restaurant -> {
|
||||
selectedRestaurant = restaurant;
|
||||
currentState = State.DETAILS;
|
||||
updateViewState();
|
||||
}));
|
||||
private void loadRestaurants() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Restaurantes");
|
||||
ref.orderByChild("category").equalTo(selectedCategory).addListenerForSingleValueEvent(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
List<Restaurant> list = new ArrayList<>();
|
||||
for (DataSnapshot ds : snapshot.getChildren()) {
|
||||
String name = ds.child("establishmentName").getValue(String.class);
|
||||
if (name == null) name = ds.child("displayName").getValue(String.class);
|
||||
String email = ds.child("email").getValue(String.class);
|
||||
String cat = ds.child("category").getValue(String.class);
|
||||
String logo = ds.child("logoUrl").getValue(String.class);
|
||||
if (name != null && email != null) {
|
||||
list.add(new Restaurant(name, cat, email, false, logo));
|
||||
}
|
||||
}
|
||||
rvRestaurants.setLayoutManager(new LinearLayoutManager(NovaReservaActivity.this));
|
||||
rvRestaurants.setAdapter(new RestaurantAdapter(list, restaurant -> {
|
||||
selectedRestaurant = restaurant;
|
||||
currentState = State.DETAILS;
|
||||
loadTables();
|
||||
updateViewState();
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
android.widget.Toast.makeText(NovaReservaActivity.this, "Erro ao carregar restaurantes.",
|
||||
android.widget.Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onCancelled(@NonNull DatabaseError error) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
Toast.makeText(NovaReservaActivity.this, "Erro ao carregar.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String selectedDate = null;
|
||||
private String selectedTime = null;
|
||||
private void loadTables() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Mesas");
|
||||
ref.addListenerForSingleValueEvent(new ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
List<Mesa> tables = new ArrayList<>();
|
||||
for (DataSnapshot ds : snapshot.getChildren()) {
|
||||
Mesa m = ds.getValue(Mesa.class);
|
||||
if (m != null && selectedRestaurant.getEmail().equalsIgnoreCase(m.getRestauranteEmail())) {
|
||||
tables.add(m);
|
||||
}
|
||||
}
|
||||
rvTables.setLayoutManager(new GridLayoutManager(NovaReservaActivity.this, 3));
|
||||
rvTables.setAdapter(new TableSelectionAdapter(tables, mesa -> selectedMesa = mesa));
|
||||
}
|
||||
|
||||
private void setupReservationOptions() {
|
||||
android.widget.Button btnDate = findViewById(R.id.btnSelectDate);
|
||||
android.widget.Button btnTime = findViewById(R.id.btnSelectTime);
|
||||
|
||||
btnDate.setOnClickListener(v -> {
|
||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||
new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
||||
btnDate.setText(selectedDate);
|
||||
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH),
|
||||
cal.get(java.util.Calendar.DAY_OF_MONTH)).show();
|
||||
@Override
|
||||
public void onCancelled(@NonNull DatabaseError error) {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
|
||||
btnTime.setOnClickListener(v -> {
|
||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
||||
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
||||
selectedTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
||||
btnTime.setText(selectedTime);
|
||||
}, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show();
|
||||
});
|
||||
|
||||
findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> saveReservation());
|
||||
}
|
||||
|
||||
private void saveReservation() {
|
||||
android.widget.EditText etPartySize = findViewById(R.id.etPartySize);
|
||||
int val = 0;
|
||||
try {
|
||||
val = Integer.parseInt(etPartySize.getText().toString());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
final int partySize = val;
|
||||
private void showDatePicker() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
new DatePickerDialog(this, (view, year, month, day) -> {
|
||||
selectedDate = String.format(Locale.getDefault(), "%02d/%02d/%d", day, month + 1, year);
|
||||
((Button)findViewById(R.id.btnSelectDate)).setText(selectedDate);
|
||||
}, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
|
||||
}
|
||||
|
||||
if (selectedDate == null || selectedTime == null || partySize == 0) {
|
||||
android.widget.Toast.makeText(this, "Por favor, selecione data, hora e número de pessoas.",
|
||||
android.widget.Toast.LENGTH_SHORT).show();
|
||||
private void showTimePicker() {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
new TimePickerDialog(this, (view, hour, min) -> {
|
||||
selectedTime = String.format(Locale.getDefault(), "%02d:%02d", hour, min);
|
||||
((Button)findViewById(R.id.btnSelectTime)).setText(selectedTime);
|
||||
}, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show();
|
||||
}
|
||||
|
||||
private void validateAndSave() {
|
||||
EditText etPartySize = findViewById(R.id.etPartySize);
|
||||
EditText etNotes = findViewById(R.id.etNotes);
|
||||
String partySizeStr = etPartySize.getText().toString();
|
||||
|
||||
if (selectedDate == null || selectedTime == null || selectedMesa == null || partySizeStr.isEmpty()) {
|
||||
Toast.makeText(this, "Preencha todos os campos e escolha a mesa.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
String restEmail = selectedRestaurant.getEmail();
|
||||
int partySize = Integer.parseInt(partySizeStr);
|
||||
if (partySize > selectedMesa.getCapacidade()) {
|
||||
Toast.makeText(this, "A mesa escolhida só suporta " + selectedMesa.getCapacidade() + " pessoas.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.VISIBLE);
|
||||
android.widget.Button btnConfirmar = findViewById(R.id.btnConfirmarReserva);
|
||||
if (btnConfirmar != null)
|
||||
btnConfirmar.setEnabled(false);
|
||||
|
||||
com.google.firebase.database.DatabaseReference mesasRef = com.google.firebase.database.FirebaseDatabase
|
||||
.getInstance().getReference("Mesas");
|
||||
|
||||
mesasRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
int totalMesas = 0;
|
||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||
com.example.pap_teste.models.Mesa m = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
||||
if (m != null && m.getRestauranteEmail() != null && restEmail.trim().equalsIgnoreCase(m.getRestauranteEmail().trim())) {
|
||||
totalMesas++;
|
||||
}
|
||||
// Conflict check
|
||||
viewModel.checkConflict(selectedDate, selectedMesa.getNumero(), isAvailable -> {
|
||||
runOnUiThread(() -> {
|
||||
if (isAvailable) {
|
||||
saveToFirebaseAndRoom(partySize, etNotes.getText().toString());
|
||||
} else {
|
||||
Toast.makeText(this, "Esta mesa já está reservada para este dia.", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
if (totalMesas == 0) {
|
||||
proceedWithReservation(partySize);
|
||||
return;
|
||||
}
|
||||
|
||||
checkReservationsAndSave(totalMesas, partySize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
proceedWithReservation(partySize);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void checkReservationsAndSave(int totalMesas, final int partySize) {
|
||||
String restEmail = selectedRestaurant.getEmail();
|
||||
com.google.firebase.database.DatabaseReference reservasRef = com.google.firebase.database.FirebaseDatabase
|
||||
.getInstance().getReference("reservas");
|
||||
|
||||
reservasRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
||||
@Override
|
||||
public void onDataChange(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
||||
int ocupadas = 0;
|
||||
java.util.Map<String, Integer> ocupacaoPorHora = new java.util.HashMap<>();
|
||||
|
||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
||||
com.example.pap_teste.models.Reserva r = ds
|
||||
.getValue(com.example.pap_teste.models.Reserva.class);
|
||||
if (r != null && r.getRestauranteEmail() != null &&
|
||||
r.getRestauranteEmail().trim().equalsIgnoreCase(restEmail.trim()) &&
|
||||
selectedDate.equals(r.getData()) && !"Cancelada".equals(r.getEstado())
|
||||
&& !"Recusada".equals(r.getEstado())) {
|
||||
int count = ocupacaoPorHora.getOrDefault(r.getHora(), 0) + 1;
|
||||
ocupacaoPorHora.put(r.getHora(), count);
|
||||
if (selectedTime.equals(r.getHora())) {
|
||||
ocupadas++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ocupadas >= totalMesas) {
|
||||
String sugestao = "";
|
||||
String[] horasComuns = { "12:00", "12:30", "13:00", "13:30", "14:00", "19:00", "19:30",
|
||||
"20:00", "20:30", "21:00", "21:30", "22:00" };
|
||||
for (String h : horasComuns) {
|
||||
if (ocupacaoPorHora.getOrDefault(h, 0) < totalMesas && !h.equals(selectedTime)) {
|
||||
sugestao = h;
|
||||
break; // Encontramos a primeira sugestão livre
|
||||
}
|
||||
}
|
||||
String msg = "Não há mesas disponíveis para as " + selectedTime + ".";
|
||||
if (!sugestao.isEmpty()) {
|
||||
msg += " Sugestão: tente reservar para as " + sugestao + ".";
|
||||
} else {
|
||||
msg += " Tente para outro dia.";
|
||||
}
|
||||
android.widget.Toast
|
||||
.makeText(NovaReservaActivity.this, msg, android.widget.Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
proceedWithReservation(partySize);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled(
|
||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
||||
proceedWithReservation(partySize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void proceedWithReservation(int partySize) {
|
||||
String userEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null
|
||||
? com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
||||
: "cliente@teste.com";
|
||||
com.google.firebase.database.DatabaseReference ref = com.google.firebase.database.FirebaseDatabase.getInstance()
|
||||
.getReference("reservas");
|
||||
private void saveToFirebaseAndRoom(int guests, String notes) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("reservas");
|
||||
String id = ref.push().getKey();
|
||||
String userEmail = FirebaseAuth.getInstance().getCurrentUser() != null ? FirebaseAuth.getInstance().getCurrentUser().getEmail() : "anon@test.com";
|
||||
|
||||
com.example.pap_teste.models.Reserva reserva = new com.example.pap_teste.models.Reserva(
|
||||
id,
|
||||
userEmail,
|
||||
selectedRestaurant.getName(),
|
||||
selectedRestaurant.getEmail(),
|
||||
selectedDate,
|
||||
selectedTime,
|
||||
partySize,
|
||||
"Pendente");
|
||||
LocalReservation local = new LocalReservation();
|
||||
local.setFirebaseId(id);
|
||||
local.setRestaurantName(selectedRestaurant.getName());
|
||||
local.setRestaurantEmail(selectedRestaurant.getEmail());
|
||||
local.setDate(selectedDate);
|
||||
local.setTime(selectedTime);
|
||||
local.setGuests(guests);
|
||||
local.setTableNumber(selectedMesa.getNumero());
|
||||
local.setNotes(notes);
|
||||
local.setStatus("PENDENTE");
|
||||
|
||||
if (id != null) {
|
||||
ref.child(id).setValue(reserva).addOnCompleteListener(task -> {
|
||||
if (progressBar != null)
|
||||
progressBar.setVisibility(android.view.View.GONE);
|
||||
android.widget.Button btnConfirmar = findViewById(R.id.btnConfirmarReserva);
|
||||
if (btnConfirmar != null)
|
||||
btnConfirmar.setEnabled(true);
|
||||
|
||||
ref.child(id).setValue(local).addOnCompleteListener(task -> {
|
||||
progressBar.setVisibility(View.GONE);
|
||||
if (task.isSuccessful()) {
|
||||
android.widget.Toast
|
||||
.makeText(NovaReservaActivity.this, "Reserva solicitada com sucesso!",
|
||||
android.widget.Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
viewModel.insert(local);
|
||||
Toast.makeText(this, "Reserva realizada com sucesso!", Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
} else {
|
||||
android.widget.Toast
|
||||
.makeText(NovaReservaActivity.this, "Erro ao salvar reserva.",
|
||||
android.widget.Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
Toast.makeText(this, "Erro ao guardar no servidor.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ public class OnboardingActivity extends AppCompatActivity {
|
||||
tagline.startAnimation(fadeIn);
|
||||
|
||||
btnGetStarted.setOnClickListener(v -> {
|
||||
startActivity(new Intent(OnboardingActivity.this, MainActivity.class));
|
||||
android.app.ActivityOptions options = android.app.ActivityOptions.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
startActivity(new Intent(OnboardingActivity.this, MainActivity.class), options.toBundle());
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import android.widget.Toast;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.firebase.auth.FirebaseAuth;
|
||||
import com.google.firebase.auth.FirebaseUser;
|
||||
|
||||
36
app/src/main/java/com/example/pap_teste/SplashActivity.java
Normal file
36
app/src/main/java/com/example/pap_teste/SplashActivity.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
public class SplashActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_splash);
|
||||
|
||||
ImageView logo = findViewById(R.id.splash_logo);
|
||||
TextView title = findViewById(R.id.splash_title);
|
||||
TextView subtitle = findViewById(R.id.splash_subtitle);
|
||||
|
||||
Animation fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
|
||||
fadeIn.setDuration(1500);
|
||||
|
||||
logo.startAnimation(fadeIn);
|
||||
title.startAnimation(fadeIn);
|
||||
subtitle.startAnimation(fadeIn);
|
||||
|
||||
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||
startActivity(new Intent(SplashActivity.this, OnboardingActivity.class));
|
||||
finish();
|
||||
}, 2500);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.example.pap_teste;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.example.pap_teste.models.Mesa;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import java.util.List;
|
||||
|
||||
public class TableSelectionAdapter extends RecyclerView.Adapter<TableSelectionAdapter.ViewHolder> {
|
||||
|
||||
private final List<Mesa> tables;
|
||||
private final OnTableClickListener listener;
|
||||
private int selectedPosition = -1;
|
||||
|
||||
public interface OnTableClickListener {
|
||||
void onTableClick(Mesa mesa);
|
||||
}
|
||||
|
||||
public TableSelectionAdapter(List<Mesa> tables, OnTableClickListener listener) {
|
||||
this.tables = tables;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_table, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Mesa mesa = tables.get(position);
|
||||
holder.txtNumber.setText("Mesa " + mesa.getNumero());
|
||||
|
||||
boolean isOccupied = "OCUPADA".equalsIgnoreCase(mesa.getEstado()) || "RESERVADA".equalsIgnoreCase(mesa.getEstado());
|
||||
holder.txtStatus.setText(mesa.getEstado());
|
||||
|
||||
if (isOccupied) {
|
||||
holder.card.setCardBackgroundColor(Color.parseColor("#F5F5F5"));
|
||||
holder.card.setStrokeColor(Color.TRANSPARENT);
|
||||
holder.txtStatus.setTextColor(Color.GRAY);
|
||||
holder.itemView.setEnabled(false);
|
||||
holder.imgStatus.setAlpha(0.3f);
|
||||
} else {
|
||||
boolean isSelected = selectedPosition == position;
|
||||
holder.card.setCardBackgroundColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorChipConfirmed) : Color.WHITE);
|
||||
holder.card.setStrokeColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorSuccess) : holder.itemView.getContext().getColor(R.color.colorBorder));
|
||||
holder.txtStatus.setTextColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorSuccess) : holder.itemView.getContext().getColor(R.color.colorTextSecondary));
|
||||
holder.itemView.setEnabled(true);
|
||||
holder.imgStatus.setAlpha(1.0f);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
int previousSelected = selectedPosition;
|
||||
selectedPosition = holder.getAdapterPosition();
|
||||
if (previousSelected != -1) notifyItemChanged(previousSelected);
|
||||
notifyItemChanged(selectedPosition);
|
||||
listener.onTableClick(mesa);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return tables.size();
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
MaterialCardView card;
|
||||
TextView txtNumber, txtStatus;
|
||||
ImageView imgStatus;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
card = itemView.findViewById(R.id.cardTable);
|
||||
txtNumber = itemView.findViewById(R.id.txtTableNumber);
|
||||
txtStatus = itemView.findViewById(R.id.txtTableStatus);
|
||||
imgStatus = itemView.findViewById(R.id.imgTableStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.example.pap_teste.data;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
import com.example.pap_teste.models.LocalReservation;
|
||||
|
||||
@Database(entities = {LocalReservation.class}, version = 1, exportSchema = false)
|
||||
public abstract class AppDatabase extends RoomDatabase {
|
||||
private static AppDatabase instance;
|
||||
|
||||
public abstract ReservationDao reservationDao();
|
||||
|
||||
public static synchronized AppDatabase getInstance(Context context) {
|
||||
if (instance == null) {
|
||||
instance = Room.databaseBuilder(context.getApplicationContext(),
|
||||
AppDatabase.class, "namesa_database")
|
||||
.fallbackToDestructiveMigration()
|
||||
.build();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.example.pap_teste.data;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.OnConflictStrategy;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
import com.example.pap_teste.models.LocalReservation;
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface ReservationDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
void insert(LocalReservation reservation);
|
||||
|
||||
@Update
|
||||
void update(LocalReservation reservation);
|
||||
|
||||
@Delete
|
||||
void delete(LocalReservation reservation);
|
||||
|
||||
@Query("SELECT * FROM local_reservations ORDER BY date DESC, time DESC")
|
||||
LiveData<List<LocalReservation>> getAllReservations();
|
||||
|
||||
@Query("SELECT * FROM local_reservations WHERE date = :date AND tableNumber = :tableId AND status != 'CANCELLED'")
|
||||
List<LocalReservation> getReservationsForTable(String date, int tableId);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.example.pap_teste.models;
|
||||
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
@Entity(tableName = "local_reservations")
|
||||
public class LocalReservation {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
private int id;
|
||||
private String firebaseId;
|
||||
private String restaurantName;
|
||||
private String restaurantEmail;
|
||||
private String date;
|
||||
private String time;
|
||||
private int guests;
|
||||
private String status;
|
||||
private int tableNumber;
|
||||
private String notes;
|
||||
|
||||
public LocalReservation() {}
|
||||
|
||||
public int getId() { return id; }
|
||||
public void setId(int id) { this.id = id; }
|
||||
|
||||
public String getFirebaseId() { return firebaseId; }
|
||||
public void setFirebaseId(String firebaseId) { this.firebaseId = firebaseId; }
|
||||
|
||||
public String getRestaurantName() { return restaurantName; }
|
||||
public void setRestaurantName(String restaurantName) { this.restaurantName = restaurantName; }
|
||||
|
||||
public String getRestaurantEmail() { return restaurantEmail; }
|
||||
public void setRestaurantEmail(String restaurantEmail) { this.restaurantEmail = restaurantEmail; }
|
||||
|
||||
public String getDate() { return date; }
|
||||
public void setDate(String date) { this.date = date; }
|
||||
|
||||
public String getTime() { return time; }
|
||||
public void setTime(String time) { this.time = time; }
|
||||
|
||||
public int getGuests() { return guests; }
|
||||
public void setGuests(int guests) { this.guests = guests; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public int getTableNumber() { return tableNumber; }
|
||||
public void setTableNumber(int tableNumber) { this.tableNumber = tableNumber; }
|
||||
|
||||
public String getNotes() { return notes; }
|
||||
public void setNotes(String notes) { this.notes = notes; }
|
||||
}
|
||||
@@ -1,14 +1,25 @@
|
||||
package com.example.pap_teste.models;
|
||||
|
||||
import com.google.firebase.Timestamp;
|
||||
|
||||
public class Reserva {
|
||||
private String id;
|
||||
private String userId;
|
||||
private String restaurantId;
|
||||
private String restaurantName;
|
||||
private String clienteEmail;
|
||||
private String restauranteName;
|
||||
private String restauranteEmail;
|
||||
private String data;
|
||||
private String hora;
|
||||
private int pessoas;
|
||||
private int guests;
|
||||
private String estado;
|
||||
private String status;
|
||||
private Timestamp date;
|
||||
private String timeSlot;
|
||||
private String specialRequests;
|
||||
private Timestamp createdAt;
|
||||
|
||||
public Reserva() {
|
||||
}
|
||||
@@ -25,27 +36,41 @@ public class Reserva {
|
||||
this.estado = estado;
|
||||
}
|
||||
|
||||
// Existing getters/setters
|
||||
public String getId() { return id; }
|
||||
public void setId(String id) { this.id = id; }
|
||||
|
||||
public String getClienteEmail() { return clienteEmail; }
|
||||
public void setClienteEmail(String clienteEmail) { this.clienteEmail = clienteEmail; }
|
||||
|
||||
public String getRestauranteName() { return restauranteName; }
|
||||
public void setRestauranteName(String restauranteName) { this.restauranteName = restauranteName; }
|
||||
|
||||
public String getRestauranteEmail() { return restauranteEmail; }
|
||||
public void setRestauranteEmail(String restauranteEmail) { this.restauranteEmail = restauranteEmail; }
|
||||
|
||||
public String getData() { return data; }
|
||||
public void setData(String data) { this.data = data; }
|
||||
|
||||
public String getHora() { return hora; }
|
||||
public void setHora(String hora) { this.hora = hora; }
|
||||
|
||||
public int getPessoas() { return pessoas; }
|
||||
public void setPessoas(int pessoas) { this.pessoas = pessoas; }
|
||||
|
||||
public String getEstado() { return estado; }
|
||||
public void setEstado(String estado) { this.estado = estado; }
|
||||
|
||||
// New getters/setters to fix ReservationActivity
|
||||
public String getUserId() { return userId != null ? userId : clienteEmail; }
|
||||
public void setUserId(String userId) { this.userId = userId; }
|
||||
public String getRestaurantId() { return restaurantId != null ? restaurantId : restauranteEmail; }
|
||||
public void setRestaurantId(String restaurantId) { this.restaurantId = restaurantId; }
|
||||
public String getRestaurantName() { return restaurantName != null ? restaurantName : restauranteName; }
|
||||
public void setRestaurantName(String restaurantName) { this.restaurantName = restaurantName; }
|
||||
public int getGuests() { return guests != 0 ? guests : pessoas; }
|
||||
public void setGuests(int guests) { this.guests = guests; }
|
||||
public String getStatus() { return status != null ? status : estado; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Timestamp getDate() { return date; }
|
||||
public void setDate(Timestamp date) { this.date = date; }
|
||||
public String getTimeSlot() { return timeSlot != null ? timeSlot : hora; }
|
||||
public void setTimeSlot(String timeSlot) { this.timeSlot = timeSlot; }
|
||||
public String getSpecialRequests() { return specialRequests; }
|
||||
public void setSpecialRequests(String specialRequests) { this.specialRequests = specialRequests; }
|
||||
public Timestamp getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Timestamp createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.example.pap_teste.viewmodels;
|
||||
|
||||
import android.app.Application;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import com.example.pap_teste.data.AppDatabase;
|
||||
import com.example.pap_teste.data.ReservationDao;
|
||||
import com.example.pap_teste.models.LocalReservation;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class ReservationViewModel extends AndroidViewModel {
|
||||
private final ReservationDao reservationDao;
|
||||
private final ExecutorService executorService;
|
||||
private final LiveData<List<LocalReservation>> allReservations;
|
||||
|
||||
public ReservationViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
AppDatabase db = AppDatabase.getInstance(application);
|
||||
reservationDao = db.reservationDao();
|
||||
allReservations = reservationDao.getAllReservations();
|
||||
executorService = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
|
||||
public LiveData<List<LocalReservation>> getAllReservations() {
|
||||
return allReservations;
|
||||
}
|
||||
|
||||
public void insert(LocalReservation reservation) {
|
||||
executorService.execute(() -> reservationDao.insert(reservation));
|
||||
}
|
||||
|
||||
public void update(LocalReservation reservation) {
|
||||
executorService.execute(() -> reservationDao.update(reservation));
|
||||
}
|
||||
|
||||
public void delete(LocalReservation reservation) {
|
||||
executorService.execute(() -> reservationDao.delete(reservation));
|
||||
}
|
||||
|
||||
public void checkConflict(String date, int tableId, OnConflictCheckListener listener) {
|
||||
executorService.execute(() -> {
|
||||
List<LocalReservation> conflicts = reservationDao.getReservationsForTable(date, tableId);
|
||||
listener.onResult(conflicts.isEmpty());
|
||||
});
|
||||
}
|
||||
|
||||
public interface OnConflictCheckListener {
|
||||
void onResult(boolean isAvailable);
|
||||
}
|
||||
}
|
||||
@@ -161,6 +161,24 @@
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
app:strokeColor="@color/colorDivider" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="Escolha a mesa"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvTables"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:clipToPadding="false"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="3"
|
||||
tools:listitem="@layout/item_table" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -180,6 +198,28 @@
|
||||
android:hint="Ex: 2"
|
||||
android:textColorHint="@color/colorTextHint"
|
||||
android:textColor="@color/colorTextPrimary" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="Notas adicionais (opcional)"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/etNotes"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_input_modern"
|
||||
android:inputType="textMultiLine"
|
||||
android:minHeight="100dp"
|
||||
android:gravity="top"
|
||||
android:padding="16dp"
|
||||
android:hint="Alguma preferência?"
|
||||
android:textColorHint="@color/colorTextHint"
|
||||
android:textColor="@color/colorTextPrimary" />
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
47
app/src/main/res/layout/activity_splash.xml
Normal file
47
app/src/main/res/layout/activity_splash.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout 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/colorPrimary">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/splash_logo"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:src="@drawable/na_mesa"
|
||||
app:layout_constraintBottom_toTopOf="@+id/splash_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/splash_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:fontFamily="sans-serif-black"
|
||||
android:text="NaMesa"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="40sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/splash_subtitle"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/splash_logo" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/splash_subtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:alpha="0.8"
|
||||
android:text="Sempre um lugar para si"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/splash_title" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
96
app/src/main/res/layout/item_reserva_local.xml
Normal file
96
app/src/main/res/layout/item_reserva_local.xml
Normal file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView 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="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginVertical="8dp"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="4dp"
|
||||
app:strokeWidth="0dp">
|
||||
|
||||
<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="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtRestauranteNome"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="Nome do Restaurante"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipStatus"
|
||||
style="@style/Widget.Material3.Chip.Suggestion"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="PENDENTE"
|
||||
app:chipBackgroundColor="@color/colorChipPending"
|
||||
android:textColor="@color/colorChipPendingText" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtDataHora"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="15/05/2026 às 20:00"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtMesaPessoas"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Mesa 5 • 4 pessoas"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:background="@color/colorDivider" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnCancelar"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Cancelar"
|
||||
android:textColor="@color/colorError" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/btnEditar"
|
||||
style="@style/Widget.Material3.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Ver Detalhes"
|
||||
android:textColor="@color/colorPrimary" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
48
app/src/main/res/layout/item_table.xml
Normal file
48
app/src/main/res/layout/item_table.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/cardTable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="4dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="2dp"
|
||||
app:strokeWidth="2dp"
|
||||
app:strokeColor="@color/colorBorder"
|
||||
app:cardBackgroundColor="@color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgTableStatus"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:src="@android:drawable/ic_menu_agenda"
|
||||
app:tint="@color/colorTextSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtTableNumber"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:text="Mesa 1"
|
||||
android:textColor="@color/colorTextPrimary"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtTableStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Livre"
|
||||
android:textColor="@color/colorTextSecondary"
|
||||
android:textSize="12sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
@@ -13,6 +13,7 @@ googleServices = "4.4.2"
|
||||
firebaseDatabase = "22.0.1"
|
||||
glide = "4.16.0"
|
||||
firebaseStorage = "21.0.1"
|
||||
room = "2.6.1"
|
||||
|
||||
[libraries]
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
@@ -27,6 +28,9 @@ firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.r
|
||||
firebase-database = { group = "com.google.firebase", name = "firebase-database", version.ref = "firebaseDatabase" }
|
||||
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
|
||||
firebase-storage = { group = "com.google.firebase", name = "firebase-storage", version.ref = "firebaseStorage" }
|
||||
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
||||
Reference in New Issue
Block a user