From c3aba5346892a29b7a7ccbb4f7ce09fb0d34743c Mon Sep 17 00:00:00 2001 From: 230409 <230409@epvc.pt> Date: Tue, 28 Apr 2026 17:03:00 +0100 Subject: [PATCH] ... --- .../pap_teste/ClientDashboardActivity.java | 456 ++++++++++-------- .../ExplorarRestaurantesActivity.java | 261 ++++++++-- .../example/pap_teste/FavoritosActivity.java | 17 +- .../pap_teste/MinhasReservasActivity.java | 15 + .../pap_teste/NovaReservaActivity.java | 14 + .../com/example/pap_teste/ReservaAdapter.java | 22 +- .../example/pap_teste/RestaurantAdapter.java | 57 ++- .../example/pap_teste/models/Restaurant.java | 7 + .../res/layout/activity_client_dashboard.xml | 429 ++++++++-------- .../layout/activity_explorar_restaurantes.xml | 153 ++++-- .../main/res/layout/activity_favoritos.xml | 74 ++- .../res/layout/activity_minhas_reservas.xml | 70 ++- .../main/res/layout/activity_nova_reserva.xml | 114 +++-- .../main/res/layout/item_reserva_cliente.xml | 103 ++-- app/src/main/res/layout/item_restaurant.xml | 143 ++++-- app/src/main/res/values/colors.xml | 29 +- 16 files changed, 1258 insertions(+), 706 deletions(-) diff --git a/app/src/main/java/com/example/pap_teste/ClientDashboardActivity.java b/app/src/main/java/com/example/pap_teste/ClientDashboardActivity.java index 70a901d..81bae4e 100644 --- a/app/src/main/java/com/example/pap_teste/ClientDashboardActivity.java +++ b/app/src/main/java/com/example/pap_teste/ClientDashboardActivity.java @@ -2,7 +2,12 @@ package com.example.pap_teste; import android.content.Intent; import android.os.Bundle; -import android.widget.Button; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.ProgressBar; import android.widget.TextView; import androidx.activity.EdgeToEdge; @@ -10,214 +15,279 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; - +import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.example.pap_teste.models.FoodCategory; + +import com.example.pap_teste.models.Restaurant; +import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.google.android.material.chip.Chip; +import com.google.android.material.chip.ChipGroup; +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.List; public class ClientDashboardActivity extends AppCompatActivity { - private String email, displayName, role; - private TextView txtGreeting; - private androidx.activity.result.ActivityResultLauncher profileLauncher; + private String email, displayName; + private TextView txtGreeting; + private ImageView imgProfile; + private EditText etSearch; + private ChipGroup chipGroupCategories; + private RecyclerView rvFeatured, rvMainRestaurants; + private ProgressBar progressBar; + private View layoutFeatured, layoutAllRestaurants; - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - EdgeToEdge.enable(this); - setContentView(R.layout.activity_client_dashboard); - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.clientRoot), (v, insets) -> { - Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); - v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); - return insets; - }); + private List allRestaurants = new ArrayList<>(); + private List filteredRestaurants = new ArrayList<>(); + + private RestaurantAdapter mainAdapter; + private FeaturedRestaurantAdapter featuredAdapter; - txtGreeting = findViewById(R.id.txtClientGreeting); + private final String[] CATEGORIES = {"Tudo", "Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas"}; + private String currentCategoryFilter = "Tudo"; + private String currentSearchFilter = ""; - email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL); - displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME); - role = getIntent().getStringExtra(MainActivity.EXTRA_ROLE); + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + EdgeToEdge.enable(this); + setContentView(R.layout.activity_client_dashboard); + + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.clientRoot), (v, insets) -> { + Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); + v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom); + return insets; + }); - profileLauncher = registerForActivityResult( - new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(), - result -> { - if (result.getResultCode() == RESULT_OK && result.getData() != null) { - String updatedName = result.getData() - .getStringExtra(MainActivity.EXTRA_DISPLAY_NAME); - if (updatedName != null) { - displayName = updatedName; - updateGreeting(); - } - } - }); + email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL); + displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME); - updateGreeting(); - fetchProfilePicture(); - setupCategories(); - setupActions(); - loadNextReservation(); + initViews(); + setupBottomNavigation(); + setupSearch(); + setupCategories(); + + updateGreeting(); + fetchProfilePicture(); + fetchRestaurants(); + } + + private void initViews() { + txtGreeting = findViewById(R.id.txtClientGreeting); + imgProfile = findViewById(R.id.imgProfile); + etSearch = findViewById(R.id.etSearch); + chipGroupCategories = findViewById(R.id.chipGroupCategories); + rvFeatured = findViewById(R.id.rvFeatured); + rvMainRestaurants = findViewById(R.id.rvMainRestaurants); + progressBar = findViewById(R.id.progressBar); + layoutFeatured = findViewById(R.id.layoutFeatured); + layoutAllRestaurants = findViewById(R.id.layoutAllRestaurants); + + rvFeatured.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); + rvMainRestaurants.setLayoutManager(new LinearLayoutManager(this)); + + // Click listener for restaurants to open booking flow + RestaurantAdapter.OnRestaurantClickListener clickListener = restaurant -> { + Intent intent = new Intent(this, ExplorarRestaurantesActivity.class); + // Reusing existing activity for details if needed, or pass data to NovaReservaActivity + // We pass the filter so it can maybe open directly or we just pass restaurant email + intent.putExtra("category_filter", restaurant.getCategory()); + startActivity(intent); + }; + + featuredAdapter = new FeaturedRestaurantAdapter(new ArrayList<>(), clickListener); + mainAdapter = new RestaurantAdapter(filteredRestaurants, clickListener); + + rvFeatured.setAdapter(featuredAdapter); + rvMainRestaurants.setAdapter(mainAdapter); + } + + private void setupBottomNavigation() { + BottomNavigationView bottomNav = findViewById(R.id.bottomNavigation); + bottomNav.setOnItemSelectedListener(item -> { + int id = item.getItemId(); + if (id == R.id.nav_home) { + return true; + } else if (id == R.id.nav_reservations) { + Intent intent = new Intent(this, MinhasReservasActivity.class); + intent.putExtra(MainActivity.EXTRA_EMAIL, email); + startActivity(intent); + return false; // Don't highlight if we open new activity + } else if (id == R.id.nav_profile) { + Intent intent = new Intent(this, ProfileDashboardActivity.class); + intent.putExtra(MainActivity.EXTRA_EMAIL, email); + intent.putExtra(MainActivity.EXTRA_DISPLAY_NAME, displayName); + startActivity(intent); + return false; + } + return false; + }); + } + + private void updateGreeting() { + if (displayName != null && !displayName.isEmpty()) { + txtGreeting.setText("Olá, " + displayName.split(" ")[0] + "!"); + } else { + txtGreeting.setText("Olá, Visitante!"); } + } - private void updateGreeting() { - txtGreeting.setText(String.format("Olá, %s", displayName != null ? displayName : "convidado")); - } + private void fetchProfilePicture() { + if (email == null) return; + String documentId = email.replace(".", "_").replace("@", "_at_"); + FirebaseDatabase.getInstance().getReference().child("users").child(documentId).child("photoUrl") + .get().addOnSuccessListener(snapshot -> { + if (!isDestroyed() && snapshot.exists() && snapshot.getValue(String.class) != null) { + com.bumptech.glide.Glide.with(this).load(snapshot.getValue(String.class)).circleCrop().into(imgProfile); + } + }); + } - private void fetchProfilePicture() { - if (email == null) return; - String documentId = email.replace(".", "_").replace("@", "_at_"); - com.google.firebase.database.FirebaseDatabase.getInstance().getReference() - .child("users").child(documentId).child("photoUrl") - .get().addOnSuccessListener(snapshot -> { - if (!isDestroyed() && snapshot.exists()) { - String photoUrl = snapshot.getValue(String.class); - if (photoUrl != null && !photoUrl.isEmpty()) { - android.widget.ImageView imgProfile = findViewById(R.id.imgProfile); - com.bumptech.glide.Glide.with(this).load(photoUrl).circleCrop().into(imgProfile); - } - } - }); - } + private void fetchRestaurants() { + progressBar.setVisibility(View.VISIBLE); + layoutFeatured.setVisibility(View.GONE); + layoutAllRestaurants.setVisibility(View.GONE); - private void setupCategories() { - RecyclerView rv = findViewById(R.id.rvCategories); - List 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)); + DatabaseReference usersRef = FirebaseDatabase.getInstance().getReference("users"); + usersRef.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) { + progressBar.setVisibility(View.GONE); + allRestaurants.clear(); - FoodCategoryAdapter adapter = new FoodCategoryAdapter(cats, category -> { - Intent intent = new Intent(this, ExplorarRestaurantesActivity.class); - intent.putExtra("category_filter", category.getName()); - startActivity(intent); - }); - rv.setAdapter(adapter); - } - - private void setupActions() { - findViewById(R.id.cardProfile).setOnClickListener(v -> { - Intent intent = new Intent(this, ProfileDashboardActivity.class); - intent.putExtra(MainActivity.EXTRA_EMAIL, email); - intent.putExtra(MainActivity.EXTRA_DISPLAY_NAME, displayName); - profileLauncher.launch(intent); - }); - - findViewById(R.id.btnVoltar).setOnClickListener(v -> finish()); - - findViewById(R.id.btnPartilhar).setOnClickListener( - v -> startActivity(new Intent(this, PartilharReservaActivity.class))); - - findViewById(R.id.btnNovaReserva) - .setOnClickListener(v -> startActivity(new Intent(this, NovaReservaActivity.class))); - - findViewById(R.id.btnMinhasReservas) - .setOnClickListener(v -> { - Intent intent = new Intent(this, MinhasReservasActivity.class); - intent.putExtra(MainActivity.EXTRA_EMAIL, email); - startActivity(intent); - }); - } - - private void loadNextReservation() { - if (email == null) return; - com.google.firebase.database.DatabaseReference ref = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas"); - ref.orderByChild("clienteEmail").equalTo(email).addValueEventListener(new com.google.firebase.database.ValueEventListener() { - @Override - public void onDataChange(com.google.firebase.database.DataSnapshot snapshot) { - com.example.pap_teste.models.Reserva proxima = null; - long timeProx = Long.MAX_VALUE; - long currentTime = System.currentTimeMillis(); - java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("dd/MM/yyyy HH:mm", java.util.Locale.getDefault()); - - 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.getEstado().startsWith("Confirmada") || r.getEstado().equals("Pendente"))) { - try { - java.util.Date rDate = sdf.parse(r.getData() + " " + r.getHora()); - if (rDate != null) { - long rTime = rDate.getTime(); - // Consider reservations from the last 2 hours and in the future - if (rTime >= currentTime - 2L * 60 * 60 * 1000) { - if (rTime < timeProx) { - timeProx = rTime; - proxima = r; - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - TextView txtTitle = findViewById(R.id.txtResTitle); - TextView txtTime = findViewById(R.id.txtResTime); - Button btnCheckIn = findViewById(R.id.btnCheckIn); - Button btnPartilhar = findViewById(R.id.btnPartilhar); - - if (proxima != null) { - txtTitle.setText(proxima.getRestauranteName()); - - // Formatting to show "Hoje", "Amanhã" or the date itself - String dateStr = proxima.getData(); - try { - java.util.Date rDate = sdf.parse(proxima.getData() + " " + proxima.getHora()); - if (rDate != null) { - java.util.Calendar cal = java.util.Calendar.getInstance(); - cal.setTime(rDate); - - java.util.Calendar today = java.util.Calendar.getInstance(); - - java.util.Calendar tmr = java.util.Calendar.getInstance(); - tmr.add(java.util.Calendar.DAY_OF_YEAR, 1); - - if (cal.get(java.util.Calendar.YEAR) == today.get(java.util.Calendar.YEAR) && cal.get(java.util.Calendar.DAY_OF_YEAR) == today.get(java.util.Calendar.DAY_OF_YEAR)) { - dateStr = "Hoje"; - } else if (cal.get(java.util.Calendar.YEAR) == tmr.get(java.util.Calendar.YEAR) && cal.get(java.util.Calendar.DAY_OF_YEAR) == tmr.get(java.util.Calendar.DAY_OF_YEAR)) { - dateStr = "Amanhã"; - } - } - } catch (Exception e) { - // ignore - } - - txtTime.setText(String.format("%s às %s • %d pessoas", dateStr, proxima.getHora(), proxima.getPessoas())); - btnCheckIn.setVisibility(android.view.View.VISIBLE); - btnPartilhar.setVisibility(android.view.View.VISIBLE); - - String encodedEmail = proxima.getRestauranteEmail().replace(".", "_").replace("@", "_at_"); - com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users").child(encodedEmail) - .child("logoUrl").get().addOnSuccessListener(s -> { - if (!isDestroyed() && s.exists() && s.getValue() != null) { - android.widget.ImageView imgResIcon = findViewById(R.id.imgResIcon); - com.bumptech.glide.Glide.with(ClientDashboardActivity.this).load(s.getValue(String.class)).circleCrop().into(imgResIcon); - } - }); - - com.example.pap_teste.models.Reserva finalProxima = proxima; - btnCheckIn.setOnClickListener(v -> { - boolean hasLocationPermission = androidx.core.app.ActivityCompat.checkSelfPermission(ClientDashboardActivity.this, android.Manifest.permission.ACCESS_FINE_LOCATION) == android.content.pm.PackageManager.PERMISSION_GRANTED; - if (!hasLocationPermission) { - android.widget.Toast.makeText(ClientDashboardActivity.this, "Dê permissões de localização para aceder ao check-in.", android.widget.Toast.LENGTH_SHORT).show(); - } else { - Intent intent = new Intent(ClientDashboardActivity.this, CheckInAntecipadoActivity.class); - intent.putExtra("restaurant_email", finalProxima.getRestauranteEmail()); - startActivity(intent); - } - }); - } else { - txtTitle.setText("Sem reservas ativas"); - txtTime.setText("Explore os restaurantes e reserve!"); - btnCheckIn.setVisibility(android.view.View.GONE); - btnPartilhar.setVisibility(android.view.View.GONE); - android.widget.ImageView imgResIcon = findViewById(R.id.imgResIcon); - imgResIcon.setImageResource(R.drawable.circle_bg); - } + for (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); + Double ratingAvg = ds.child("ratingAvg").getValue(Double.class); + Integer ratingCount = ds.child("ratingCount").getValue(Integer.class); + + if (name != null && email != null) { + Restaurant r = new Restaurant(name, cat != null ? cat : "Vário", email, false, logoUrl); + if (ratingAvg != null) r.setRatingAvg(ratingAvg); + if (ratingCount != null) r.setRatingCount(ratingCount); + allRestaurants.add(r); } - @Override - public void onCancelled(com.google.firebase.database.DatabaseError error) {} - }); + } + } + applyFilters(); + } + + @Override + public void onCancelled(@androidx.annotation.NonNull DatabaseError error) { + progressBar.setVisibility(View.GONE); + } + }); + } + + private void setupCategories() { + for (String category : CATEGORIES) { + Chip chip = new Chip(this); + chip.setText(category); + chip.setCheckable(true); + chip.setClickable(true); + // Default styling + chip.setChipBackgroundColorResource(R.color.colorSurface); + chip.setTextColor(getResources().getColor(R.color.colorTextSecondary)); + chip.setChipStrokeWidth(0f); + + if (category.equals("Tudo")) { + chip.setChecked(true); + chip.setChipBackgroundColorResource(R.color.colorPrimary); + chip.setTextColor(getResources().getColor(R.color.white)); + } + + chip.setOnCheckedChangeListener((buttonView, isChecked) -> { + if (isChecked) { + // Reset all other chips visual state + for (int i = 0; i < chipGroupCategories.getChildCount(); i++) { + Chip c = (Chip) chipGroupCategories.getChildAt(i); + if (c != chip) { + c.setChipBackgroundColorResource(R.color.colorSurface); + c.setTextColor(getResources().getColor(R.color.colorTextSecondary)); + } + } + chip.setChipBackgroundColorResource(R.color.colorPrimary); + chip.setTextColor(getResources().getColor(R.color.white)); + + currentCategoryFilter = category; + applyFilters(); + } else if (currentCategoryFilter.equals(category)) { + // Prevent unchecking the currently selected chip + chip.setChecked(true); + } + }); + chipGroupCategories.addView(chip); } + } + + private void setupSearch() { + 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) { + currentSearchFilter = s.toString().toLowerCase().trim(); + applyFilters(); + } + + @Override + public void afterTextChanged(Editable s) {} + }); + } + + private void applyFilters() { + filteredRestaurants.clear(); + String normalizedSearch = normalizeString(currentSearchFilter); + + for (Restaurant r : allRestaurants) { + boolean matchesCategory = currentCategoryFilter.equals("Tudo") || currentCategoryFilter.equals(r.getCategory()); + + String normalizedName = normalizeString(r.getName()); + boolean matchesSearch = currentSearchFilter.isEmpty() || normalizedName.contains(normalizedSearch); + + if (matchesCategory && matchesSearch) { + filteredRestaurants.add(r); + } + } + + mainAdapter.notifyDataSetChanged(); + + // Update featured (just the first 3 for demo, or based on a specific logic) + List featuredList = new ArrayList<>(); + for (int i = 0; i < Math.min(3, filteredRestaurants.size()); i++) { + featuredList.add(filteredRestaurants.get(i)); + } + + featuredAdapter = new FeaturedRestaurantAdapter(featuredList, mainAdapter instanceof RestaurantAdapter ? + restaurant -> { + Intent intent = new Intent(this, ExplorarRestaurantesActivity.class); + intent.putExtra("category_filter", restaurant.getCategory()); + startActivity(intent); + } : null); + rvFeatured.setAdapter(featuredAdapter); + + layoutFeatured.setVisibility(featuredList.isEmpty() ? View.GONE : View.VISIBLE); + layoutAllRestaurants.setVisibility(filteredRestaurants.isEmpty() ? View.GONE : View.VISIBLE); + } + + private String normalizeString(String str) { + if (str == null) return ""; + // Remove accents and special marks, and convert to lowercase + String normalized = java.text.Normalizer.normalize(str, java.text.Normalizer.Form.NFD); + normalized = normalized.replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); + return normalized.toLowerCase().trim(); + } } diff --git a/app/src/main/java/com/example/pap_teste/ExplorarRestaurantesActivity.java b/app/src/main/java/com/example/pap_teste/ExplorarRestaurantesActivity.java index 568eded..dd2b800 100644 --- a/app/src/main/java/com/example/pap_teste/ExplorarRestaurantesActivity.java +++ b/app/src/main/java/com/example/pap_teste/ExplorarRestaurantesActivity.java @@ -22,6 +22,7 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { private android.view.View scrollReservaDetails; private androidx.recyclerview.widget.RecyclerView rvRestaurants; private android.widget.TextView txtTitle; + private android.widget.ProgressBar progressBar; private String selectedDate = null; private String selectedTime = null; @@ -52,6 +53,7 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { rvRestaurants = findViewById(R.id.rvRestaurants); scrollReservaDetails = findViewById(R.id.scrollReservaDetails); txtTitle = findViewById(R.id.txtTituloExplorar); + progressBar = findViewById(R.id.progressBar); Button back = findViewById(R.id.btnVoltar); if (back != null) { @@ -82,9 +84,39 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { } else { txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : "")); setupReservationOptions(); + loadRestaurantRating(); } } + private void loadRestaurantRating() { + if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return; + String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_"); + + android.widget.TextView txtAvgRating = findViewById(R.id.txtAvgRating); + android.widget.TextView txtTotalReviews = findViewById(R.id.txtTotalReviews); + + com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users") + .child(encodedEmail) + .addValueEventListener(new com.google.firebase.database.ValueEventListener() { + @Override + public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) { + Double avg = snapshot.child("ratingAvg").getValue(Double.class); + Integer count = snapshot.child("ratingCount").getValue(Integer.class); + + if (avg != null && count != null && count > 0) { + txtAvgRating.setText(String.format(java.util.Locale.getDefault(), "%.1f", avg)); + txtTotalReviews.setText(String.format(java.util.Locale.getDefault(), "(%d avaliações)", count)); + } else { + txtAvgRating.setText("0.0"); + txtTotalReviews.setText("(0 avaliações)"); + } + } + + @Override + public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {} + }); + } + private void setupRestaurantList() { String filter = getIntent().getStringExtra("category_filter"); java.util.List restaurantsList = new java.util.ArrayList<>(); @@ -95,9 +127,12 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { query = usersRef.orderByChild("category").equalTo(filter); } + if (progressBar != null) progressBar.setVisibility(android.view.View.VISIBLE); + query.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); restaurantsList.clear(); for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) { String role = ds.child("role").getValue(String.class); @@ -126,6 +161,7 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { @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(ExplorarRestaurantesActivity.this, "Erro ao carregar restaurantes.", android.widget.Toast.LENGTH_SHORT).show(); } }); @@ -189,32 +225,69 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return; String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_"); + android.view.View dialogView = getLayoutInflater().inflate(R.layout.dialog_reviews_list, null); + androidx.recyclerview.widget.RecyclerView rvReviews = dialogView.findViewById(R.id.rvReviewsList); + android.widget.TextView txtEmpty = dialogView.findViewById(R.id.txtEmptyReviews); + android.widget.Button btnClose = dialogView.findViewById(R.id.btnCloseReviews); + android.widget.Button btnAdd = dialogView.findViewById(R.id.btnAddReview); + + rvReviews.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(this)); + + androidx.appcompat.app.AlertDialog dialog = new androidx.appcompat.app.AlertDialog.Builder(this) + .setView(dialogView) + .create(); + dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); + + btnClose.setOnClickListener(v -> dialog.dismiss()); + btnAdd.setOnClickListener(v -> { + dialog.dismiss(); + addReviewDialog(); + }); + com.google.firebase.database.DatabaseReference reviewsRef = com.google.firebase.database.FirebaseDatabase.getInstance() .getReference("reviews").child(encodedEmail); - reviewsRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() { + String currentUserEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null ? + com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail() : null; + + reviewsRef.addValueEventListener(new com.google.firebase.database.ValueEventListener() { @Override public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) { - java.util.List reviewsList = new java.util.ArrayList<>(); + java.util.List reviewsList = new java.util.ArrayList<>(); for (com.google.firebase.database.DataSnapshot dst : snapshot.getChildren()) { String author = dst.child("author").getValue(String.class); String text = dst.child("text").getValue(String.class); - if (author != null && text != null) { - reviewsList.add(author + "\n" + text); - } + String uEmail = dst.child("userEmail").getValue(String.class); + Float rating = dst.child("rating").getValue(Float.class); + + if (rating == null) rating = 0f; + + com.example.pap_teste.models.Review rev = new com.example.pap_teste.models.Review( + dst.getKey(), author, text, rating, uEmail); + reviewsList.add(rev); } - String[] reviewsArray = reviewsList.toArray(new String[0]); - androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(ExplorarRestaurantesActivity.this) - .setTitle("Avaliações") - .setItems(reviewsArray, null) - .setPositiveButton("Fechar", null) - .setNeutralButton("Adicionar", (dialog, which) -> addReviewDialog()); - if (reviewsList.isEmpty()) { - builder.setMessage("Ainda sem avaliações."); + txtEmpty.setVisibility(android.view.View.VISIBLE); + rvReviews.setVisibility(android.view.View.GONE); + } else { + txtEmpty.setVisibility(android.view.View.GONE); + rvReviews.setVisibility(android.view.View.VISIBLE); } - builder.show(); + + ReviewAdapter adapter = new ReviewAdapter(reviewsList, currentUserEmail, review -> { + // Confirmação de Apagar + new androidx.appcompat.app.AlertDialog.Builder(ExplorarRestaurantesActivity.this) + .setTitle("Apagar Avaliação") + .setMessage("Tens a certeza que queres apagar esta avaliação?") + .setPositiveButton("Sim", (d, w) -> { + deleteReview(review.getKey()); + d.dismiss(); + }) + .setNegativeButton("Não", null) + .show(); + }); + rvReviews.setAdapter(adapter); } @Override @@ -222,49 +295,157 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity { android.widget.Toast.makeText(ExplorarRestaurantesActivity.this, "Erro ao carregar avaliações.", android.widget.Toast.LENGTH_SHORT).show(); } }); + + dialog.show(); + } + + private void deleteReview(String reviewKey) { + if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return; + String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_"); + + com.google.firebase.database.DatabaseReference reviewsRef = com.google.firebase.database.FirebaseDatabase.getInstance() + .getReference("reviews").child(encodedEmail); + + reviewsRef.child(reviewKey).removeValue().addOnSuccessListener(aVoid -> { + com.google.android.material.snackbar.Snackbar.make(findViewById(R.id.explorarRoot), "Avaliação removida", com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show(); + recalculateRestaurantAverage(encodedEmail); + }); + } + + private void recalculateRestaurantAverage(String encodedEmail) { + com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reviews").child(encodedEmail) + .addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() { + @Override + public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) { + double totalRating = 0; + int count = 0; + for (com.google.firebase.database.DataSnapshot dst : snapshot.getChildren()) { + Float r = dst.child("rating").getValue(Float.class); + if (r != null) { + totalRating += r; + count++; + } + } + + double newAvg = count > 0 ? (totalRating / count) : 0.0; + + java.util.Map updates = new java.util.HashMap<>(); + updates.put("ratingAvg", newAvg); + updates.put("ratingCount", count); + + com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users") + .child(encodedEmail).updateChildren(updates); + } + + @Override + public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {} + }); } private void addReviewDialog() { if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return; - android.widget.EditText input = new android.widget.EditText(this); - input.setHint("Escreva a sua avaliação aqui..."); - new androidx.appcompat.app.AlertDialog.Builder(this) - .setTitle("Adicionar Avaliação") - .setView(input) - .setPositiveButton("Enviar", (dialog, which) -> { - String revText = input.getText().toString().trim(); - if (!revText.isEmpty()) { - com.google.firebase.auth.FirebaseUser currentUser = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser(); - if (currentUser != null && currentUser.getEmail() != null) { - String userDoc = currentUser.getEmail().replace(".", "_").replace("@", "_at_"); - com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users").child(userDoc).get().addOnSuccessListener(snapshot -> { - String authorName = snapshot.exists() && snapshot.child("displayName").getValue(String.class) != null - ? snapshot.child("displayName").getValue(String.class) - : "Visitante"; - submitReviewToFirebase(authorName, revText); - }).addOnFailureListener(e -> submitReviewToFirebase("Visitante", revText)); - } else { - submitReviewToFirebase("Visitante", revText); - } - } - }) - .setNegativeButton("Cancelar", null) - .show(); + android.view.View dialogView = getLayoutInflater().inflate(R.layout.dialog_leave_review, null); + com.example.pap_teste.components.InteractiveRatingBar ratingBar = dialogView.findViewById(R.id.interactiveRatingBar); + android.widget.EditText input = dialogView.findViewById(R.id.etReviewComment); + android.widget.Button btnSubmit = dialogView.findViewById(R.id.btnSubmitReview); + android.widget.Button btnCancel = dialogView.findViewById(R.id.btnCancelReview); + + btnSubmit.setEnabled(false); + btnSubmit.setAlpha(0.5f); + + input.addTextChangedListener(new android.text.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) { + boolean hasText = s != null && s.toString().trim().length() > 0; + btnSubmit.setEnabled(hasText); + btnSubmit.setAlpha(hasText ? 1.0f : 0.5f); + } + + @Override + public void afterTextChanged(android.text.Editable s) {} + }); + + androidx.appcompat.app.AlertDialog dialog = new androidx.appcompat.app.AlertDialog.Builder(this) + .setView(dialogView) + .create(); + + dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); + + btnCancel.setOnClickListener(v -> dialog.dismiss()); + btnSubmit.setOnClickListener(v -> { + float rating = (float) ratingBar.getRating(); + String revText = input.getText().toString().trim(); + + com.google.firebase.auth.FirebaseUser currentUser = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser(); + if (currentUser != null && currentUser.getEmail() != null) { + String userDoc = currentUser.getEmail().replace(".", "_").replace("@", "_at_"); + com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users").child(userDoc).get().addOnSuccessListener(snapshot -> { + String authorName = snapshot.exists() && snapshot.child("displayName").getValue(String.class) != null + ? snapshot.child("displayName").getValue(String.class) + : "Visitante"; + submitReviewToFirebase(authorName, revText, rating); + dialog.dismiss(); + }).addOnFailureListener(e -> { + submitReviewToFirebase("Visitante", revText, rating); + dialog.dismiss(); + }); + } else { + submitReviewToFirebase("Visitante", revText, rating); + dialog.dismiss(); + } + }); + + dialog.show(); } - private void submitReviewToFirebase(String authorName, String revText) { + private void submitReviewToFirebase(String authorName, String revText, float newRating) { String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_"); + + com.google.firebase.auth.FirebaseUser currentUser = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser(); + String uEmail = currentUser != null ? currentUser.getEmail() : null; + + // 1. Guardar a Review java.util.Map review = new java.util.HashMap<>(); review.put("author", authorName); review.put("text", revText); + review.put("rating", newRating); + review.put("userEmail", uEmail); + review.put("timestamp", com.google.firebase.database.ServerValue.TIMESTAMP); com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reviews") .child(encodedEmail).push().setValue(review).addOnCompleteListener(task -> { if (task.isSuccessful()) { - android.widget.Toast.makeText(this, "Avaliação enviada!", android.widget.Toast.LENGTH_SHORT).show(); + com.google.android.material.snackbar.Snackbar.make(findViewById(R.id.explorarRoot), "Obrigado pela tua avaliação!", com.google.android.material.snackbar.Snackbar.LENGTH_LONG).show(); } }); + + // 2. Atualizar Média no Restaurante + com.google.firebase.database.DatabaseReference restRef = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users").child(encodedEmail); + restRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() { + @Override + public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) { + Double currentAvg = snapshot.child("ratingAvg").getValue(Double.class); + Integer currentCount = snapshot.child("ratingCount").getValue(Integer.class); + + if (currentAvg == null) currentAvg = 0.0; + if (currentCount == null) currentCount = 0; + + double newAvg = ((currentAvg * currentCount) + newRating) / (currentCount + 1); + + java.util.Map updates = new java.util.HashMap<>(); + updates.put("ratingAvg", newAvg); + updates.put("ratingCount", currentCount + 1); + + restRef.updateChildren(updates); + } + + @Override + public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {} + }); } private void saveReservation() { diff --git a/app/src/main/java/com/example/pap_teste/FavoritosActivity.java b/app/src/main/java/com/example/pap_teste/FavoritosActivity.java index a10be70..a3e599f 100644 --- a/app/src/main/java/com/example/pap_teste/FavoritosActivity.java +++ b/app/src/main/java/com/example/pap_teste/FavoritosActivity.java @@ -29,6 +29,8 @@ public class FavoritosActivity extends AppCompatActivity { private androidx.recyclerview.widget.RecyclerView rv; private RestaurantAdapter adapter; private List list; + private android.widget.ProgressBar progressBar; + private android.view.View emptyState; @Override protected void onCreate(Bundle savedInstanceState) { @@ -47,8 +49,15 @@ public class FavoritosActivity extends AppCompatActivity { } rv = findViewById(R.id.rvFavoritos); + progressBar = findViewById(R.id.progressBar); + emptyState = findViewById(R.id.emptyState); + list = new ArrayList<>(); - adapter = new RestaurantAdapter(list, null); + adapter = new RestaurantAdapter(list, restaurant -> { + android.content.Intent intent = new android.content.Intent(this, ExplorarRestaurantesActivity.class); + intent.putExtra("category_filter", restaurant.getCategory()); // just as demo + startActivity(intent); + }); rv.setAdapter(adapter); setupFavoritesList(); @@ -64,6 +73,7 @@ public class FavoritosActivity extends AppCompatActivity { favRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { + if (progressBar != null) progressBar.setVisibility(android.view.View.GONE); list.clear(); for (DataSnapshot ds : snapshot.getChildren()) { Restaurant restaurant = ds.getValue(Restaurant.class); @@ -72,10 +82,15 @@ public class FavoritosActivity extends AppCompatActivity { } } adapter.notifyDataSetChanged(); + + if (emptyState != null) { + emptyState.setVisibility(list.isEmpty() ? android.view.View.VISIBLE : android.view.View.GONE); + } } @Override public void onCancelled(@NonNull DatabaseError error) { + if (progressBar != null) progressBar.setVisibility(android.view.View.GONE); Toast.makeText(FavoritosActivity.this, "Erro ao carregar favoritos.", Toast.LENGTH_SHORT).show(); } }); diff --git a/app/src/main/java/com/example/pap_teste/MinhasReservasActivity.java b/app/src/main/java/com/example/pap_teste/MinhasReservasActivity.java index a2fa358..4c2fdec 100644 --- a/app/src/main/java/com/example/pap_teste/MinhasReservasActivity.java +++ b/app/src/main/java/com/example/pap_teste/MinhasReservasActivity.java @@ -30,6 +30,8 @@ public class MinhasReservasActivity extends AppCompatActivity { private final List reservaList = new ArrayList<>(); private DatabaseReference databaseReference; private String clientEmail; + private android.widget.ProgressBar progressBar; + private android.view.View emptyState; @Override protected void onCreate(Bundle savedInstanceState) { @@ -56,6 +58,8 @@ public class MinhasReservasActivity extends AppCompatActivity { } rvMinhasReservas = findViewById(R.id.rvMinhasReservas); + progressBar = findViewById(R.id.progressBar); + emptyState = findViewById(R.id.emptyState); Button btnVoltar = findViewById(R.id.btnVoltar); btnVoltar.setOnClickListener(v -> finish()); @@ -82,10 +86,14 @@ public class MinhasReservasActivity extends AppCompatActivity { private void loadReservations() { databaseReference = FirebaseDatabase.getInstance().getReference("reservas"); + if (progressBar != null) progressBar.setVisibility(android.view.View.VISIBLE); + if (emptyState != null) emptyState.setVisibility(android.view.View.GONE); + databaseReference.orderByChild("clienteEmail").equalTo(clientEmail) .addValueEventListener(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { + if (progressBar != null) progressBar.setVisibility(android.view.View.GONE); reservaList.clear(); for (DataSnapshot dataSnapshot : snapshot.getChildren()) { Reserva reserva = dataSnapshot.getValue(Reserva.class); @@ -93,11 +101,18 @@ public class MinhasReservasActivity extends AppCompatActivity { reservaList.add(reserva); } } + // Order reservations (newest first based on ID or we can just reverse the list) + java.util.Collections.reverse(reservaList); adapter.notifyDataSetChanged(); + + if (emptyState != null) { + emptyState.setVisibility(reservaList.isEmpty() ? android.view.View.VISIBLE : android.view.View.GONE); + } } @Override public void onCancelled(@NonNull DatabaseError error) { + if (progressBar != null) progressBar.setVisibility(android.view.View.GONE); Toast.makeText(MinhasReservasActivity.this, "Erro ao carregar reservas.", Toast.LENGTH_SHORT) .show(); } diff --git a/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java b/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java index 435e2ab..e58d341 100644 --- a/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java +++ b/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java @@ -24,6 +24,7 @@ public class NovaReservaActivity extends AppCompatActivity { private androidx.recyclerview.widget.RecyclerView rvCategories, rvRestaurants; private android.view.View scrollNovaReserva; private android.widget.TextView txtTitle; + private android.widget.ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { @@ -40,6 +41,7 @@ public class NovaReservaActivity extends AppCompatActivity { rvRestaurants = findViewById(R.id.rvRestaurants); scrollNovaReserva = findViewById(R.id.scrollNovaReserva); txtTitle = findViewById(R.id.txtTituloNovaReserva); + progressBar = findViewById(R.id.progressBar); Button back = findViewById(R.id.btnVoltar); if (back != null) { @@ -101,9 +103,12 @@ public class NovaReservaActivity extends AppCompatActivity { java.util.List filteredList = new java.util.ArrayList<>(); com.google.firebase.database.DatabaseReference usersRef = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("users"); + 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); @@ -132,6 +137,7 @@ public class NovaReservaActivity extends AppCompatActivity { @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(); } }); @@ -179,6 +185,10 @@ public class NovaReservaActivity extends AppCompatActivity { String restEmail = selectedRestaurant.getEmail(); + 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() { @@ -276,6 +286,10 @@ public class NovaReservaActivity extends AppCompatActivity { 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); + if (task.isSuccessful()) { android.widget.Toast .makeText(NovaReservaActivity.this, "Reserva solicitada com sucesso!", android.widget.Toast.LENGTH_SHORT) diff --git a/app/src/main/java/com/example/pap_teste/ReservaAdapter.java b/app/src/main/java/com/example/pap_teste/ReservaAdapter.java index 9dddf7d..4c0ad77 100644 --- a/app/src/main/java/com/example/pap_teste/ReservaAdapter.java +++ b/app/src/main/java/com/example/pap_teste/ReservaAdapter.java @@ -41,7 +41,25 @@ public class ReservaAdapter extends RecyclerView.Adapter 0) { + holder.txtRating.setText(String.format(java.util.Locale.getDefault(), "%.1f", restaurant.getRatingAvg())); + } else { + holder.txtRating.setText("Novo"); + } + } + if (restaurant.getLogoUrl() != null && !restaurant.getLogoUrl().isEmpty()) { com.bumptech.glide.Glide.with(holder.itemView.getContext()) .load(restaurant.getLogoUrl()) @@ -61,6 +70,14 @@ public class RestaurantAdapter extends RecyclerView.Adapter { + if (listener != null) { + listener.onRestaurantClick(restaurant); + } + }); + } + FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); if (user != null && user.getEmail() != null && restaurant.getEmail() != null) { String encodedUserEmail = user.getEmail().replace(".", "_").replace("@", "_at_"); @@ -68,13 +85,17 @@ public class RestaurantAdapter extends RecyclerView.Adapter { - favRef.addListenerForSingleValueEvent(new ValueEventListener() { - @Override - public void onDataChange(@NonNull DataSnapshot snapshot) { - if (snapshot.exists()) { - favRef.removeValue(); - } else { - favRef.setValue(restaurant); - } - } - - @Override - public void onCancelled(@NonNull DatabaseError error) { } - }); + // Optimistic UI update + boolean isFav = holder.btnFavorite.getTag() != null && (Boolean) holder.btnFavorite.getTag(); + boolean newFavState = !isFav; + holder.btnFavorite.setTag(newFavState); + + if (newFavState) { + holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on); + holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat.getColor(holder.itemView.getContext(), R.color.colorPrimary)); + favRef.setValue(restaurant); + com.google.android.material.snackbar.Snackbar.make(v, "Adicionado aos Favoritos!", com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show(); + } else { + holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off); + holder.btnFavorite.clearColorFilter(); + favRef.removeValue(); + com.google.android.material.snackbar.Snackbar.make(v, "Removido dos Favoritos.", com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show(); + } }); } } @@ -106,15 +130,18 @@ public class RestaurantAdapter extends RecyclerView.Adapter - - - - - - - - - - + app:layout_constraintEnd_toEndOf="parent"> + android:paddingBottom="24dp"> - - - - + + android:paddingHorizontal="24dp" + android:paddingTop="32dp"> - - + + + + + + + + + + + + + android:layout_marginTop="24dp" + app:cardCornerRadius="16dp" + app:cardElevation="4dp" + app:strokeWidth="0dp" + app:cardBackgroundColor="@color/colorSurface"> + android:layout_height="match_parent" + android:orientation="horizontal" + android:gravity="center_vertical" + android:paddingHorizontal="16dp"> - + + - - - - - - - - - - - - - - - - + android:layout_height="match_parent" + android:layout_marginStart="12dp" + android:background="@null" + android:hint="Pesquisar restaurantes..." + android:textColor="@color/colorTextPrimary" + android:textColorHint="@color/colorTextHint" + android:textSize="16sp" + android:maxLines="1" + android:inputType="text" /> - - - - + + android:layout_marginTop="24dp" + android:scrollbars="none" + android:clipToPadding="false" + android:paddingHorizontal="20dp"> - - - - - + + + + - - - - - - - - - - - - + + android:layout_marginTop="40dp" + android:visibility="visible" + android:indeterminateTint="@color/colorPrimary" /> + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_explorar_restaurantes.xml b/app/src/main/res/layout/activity_explorar_restaurantes.xml index c6d38df..c1938ef 100644 --- a/app/src/main/res/layout/activity_explorar_restaurantes.xml +++ b/app/src/main/res/layout/activity_explorar_restaurantes.xml @@ -8,60 +8,77 @@ android:background="@color/colorBackground" tools:context=".ExplorarRestaurantesActivity"> + + android:textStyle="bold" + android:fontFamily="sans-serif" + app:layout_constraintTop_toTopOf="@id/btnVoltar" + app:layout_constraintBottom_toBottomOf="@id/btnVoltar" + app:layout_constraintStart_toEndOf="@id/btnVoltar" + app:layout_constraintEnd_toEndOf="parent" /> + + + + + + app:layout_constraintTop_toBottomOf="@id/btnVoltar"> + + android:layout_marginTop="16dp" + android:gravity="center_vertical"> + + + + + + + + + + @@ -103,13 +156,15 @@ app:strokeColor="@color/colorError" /> + + android:textSize="20sp" + android:textStyle="bold" + android:layout_marginBottom="24dp" /> + + + android:textColor="@color/colorTextPrimary" + app:strokeColor="@color/colorDivider" /> + android:textColor="@color/colorTextSecondary" + android:textSize="14sp" /> + android:textColor="@color/colorTextPrimary" + app:strokeColor="@color/colorDivider" /> + android:textColor="@color/colorTextSecondary" + android:textSize="14sp" /> @@ -182,15 +246,16 @@ + android:textStyle="bold" + android:textSize="18sp" /> diff --git a/app/src/main/res/layout/activity_favoritos.xml b/app/src/main/res/layout/activity_favoritos.xml index cfce089..f997df7 100644 --- a/app/src/main/res/layout/activity_favoritos.xml +++ b/app/src/main/res/layout/activity_favoritos.xml @@ -8,44 +8,82 @@ android:background="@color/colorBackground" tools:context=".FavoritosActivity"> + + + + + app:layout_constraintEnd_toEndOf="parent" /> + + + + + + + + diff --git a/app/src/main/res/layout/activity_minhas_reservas.xml b/app/src/main/res/layout/activity_minhas_reservas.xml index b113504..4fe4313 100644 --- a/app/src/main/res/layout/activity_minhas_reservas.xml +++ b/app/src/main/res/layout/activity_minhas_reservas.xml @@ -8,41 +8,81 @@ android:background="@color/colorBackground" tools:context=".MinhasReservasActivity"> + + + + + app:layout_constraintEnd_toEndOf="parent" /> + + + + + + + + + + + + + app:layout_constraintEnd_toEndOf="parent" /> + app:layout_constraintTop_toBottomOf="@id/btnVoltar"> + android:textSize="20sp" + android:textStyle="bold" + android:layout_marginBottom="24dp" /> + + + android:textColor="@color/colorTextPrimary" + app:strokeColor="@color/colorDivider" /> + android:textColor="@color/colorTextSecondary" + android:textSize="14sp" /> + android:textColor="@color/colorTextPrimary" + app:strokeColor="@color/colorDivider" /> + android:textColor="@color/colorTextSecondary" + android:textSize="14sp" /> @@ -164,15 +187,16 @@ + android:textStyle="bold" + android:textSize="18sp" /> diff --git a/app/src/main/res/layout/item_reserva_cliente.xml b/app/src/main/res/layout/item_reserva_cliente.xml index 767fb97..71c0035 100644 --- a/app/src/main/res/layout/item_reserva_cliente.xml +++ b/app/src/main/res/layout/item_reserva_cliente.xml @@ -3,13 +3,12 @@ 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_marginHorizontal="20dp" android:layout_marginVertical="8dp" - app:cardCornerRadius="16dp" - app:cardElevation="4dp" + app:cardCornerRadius="20dp" + app:cardElevation="2dp" app:cardBackgroundColor="@color/colorSurface" - app:strokeWidth="1dp" - app:strokeColor="@color/colorDivider"> + app:strokeWidth="0dp"> - + android:orientation="horizontal" + android:gravity="center_vertical"> + + - - - + + + + + + + + + + + + app:strokeWidth="0dp"> - + android:layout_height="wrap_content"> - - - - - - - + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" /> - + android:src="@android:drawable/btn_star_big_off" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:tint="@color/colorTextPrimary" /> + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index b961208..fb0d14f 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,23 +1,30 @@ - - #06C167 - #05A357 - #06C167 + + #FF5A5F + #FF3B30 + #FF5A5F #FF000000 #FFFFFFFF - #F8FAFC + #F7F7F9 #FFFFFF - #1E293B - #64748B + #1A1A1A + #757575 + #BDBDBD - #10B981 - #EF4444 - #F59E0B - #E2E8F0 + #34C759 + #FF3B30 + #FFCC00 + #E0E0E0 + #FFE0B2 + #E65100 + #C8E6C9 + #1B5E20 + #FFCDD2 + #B71C1C #BE1F13