diff --git a/app/src/main/java/com/example/pap_teste/DefinicoesAdminActivity.java b/app/src/main/java/com/example/pap_teste/DefinicoesAdminActivity.java index 719b7a7..794c2e6 100644 --- a/app/src/main/java/com/example/pap_teste/DefinicoesAdminActivity.java +++ b/app/src/main/java/com/example/pap_teste/DefinicoesAdminActivity.java @@ -31,6 +31,15 @@ import com.google.firebase.storage.StorageReference; import java.util.UUID; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.view.LayoutInflater; +import android.view.View; +import android.app.TimePickerDialog; +import java.util.ArrayList; +import java.util.List; +import com.example.pap_teste.models.ScheduleDay; public class DefinicoesAdminActivity extends AppCompatActivity { @@ -42,6 +51,9 @@ public class DefinicoesAdminActivity extends AppCompatActivity { private String photoUrl; private ActivityResultLauncher imagePickerLauncher; private String[] categories = {"Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas", "Italiana", "Moderna"}; + private String[] dayNames = {"Domingo", "Segunda-feira", "Terça-feira", "Quarta-feira", "Quinta-feira", "Sexta-feira", "Sábado"}; + private String[] dayKeys = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"}; + private List scheduleViews = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -105,6 +117,29 @@ public class DefinicoesAdminActivity extends AppCompatActivity { }).show(); }); + LinearLayout containerSchedule = findViewById(R.id.containerSchedule); + LayoutInflater inflater = LayoutInflater.from(this); + for (int i = 0; i < dayNames.length; i++) { + View view = inflater.inflate(R.layout.item_schedule_day, containerSchedule, false); + TextView tvDayName = view.findViewById(R.id.tvDayName); + TextView tvOpenTime = view.findViewById(R.id.tvOpenTime); + TextView tvCloseTime = view.findViewById(R.id.tvCloseTime); + CheckBox cbClosed = view.findViewById(R.id.cbClosed); + + tvDayName.setText(dayNames[i]); + + tvOpenTime.setOnClickListener(v -> showTimePicker(tvOpenTime)); + tvCloseTime.setOnClickListener(v -> showTimePicker(tvCloseTime)); + + cbClosed.setOnCheckedChangeListener((buttonView, isChecked) -> { + tvOpenTime.setEnabled(!isChecked); + tvCloseTime.setEnabled(!isChecked); + }); + + containerSchedule.addView(view); + scheduleViews.add(view); + } + loadCurrentSettings(); btnSave.setOnClickListener(v -> saveSettings()); @@ -130,6 +165,13 @@ public class DefinicoesAdminActivity extends AppCompatActivity { builder.show(); } + private void showTimePicker(TextView targetTextView) { + java.util.Calendar cal = java.util.Calendar.getInstance(); + new TimePickerDialog(this, (view, hourOfDay, minute) -> { + targetTextView.setText(String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute)); + }, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show(); + } + private void uploadImageToFirebase(Uri imageUri) { if (documentId == null) return; @@ -179,6 +221,27 @@ public class DefinicoesAdminActivity extends AppCompatActivity { Glide.with(DefinicoesAdminActivity.this).load(photoUrl).circleCrop().into(imgLogo); } } + if (snapshot.hasChild("schedule")) { + for (int i = 0; i < dayKeys.length; i++) { + DataSnapshot daySnapshot = snapshot.child("schedule").child(dayKeys[i]); + if (daySnapshot.exists()) { + ScheduleDay sd = daySnapshot.getValue(ScheduleDay.class); + if (sd != null) { + View view = scheduleViews.get(i); + CheckBox cbClosed = view.findViewById(R.id.cbClosed); + TextView tvOpenTime = view.findViewById(R.id.tvOpenTime); + TextView tvCloseTime = view.findViewById(R.id.tvCloseTime); + + cbClosed.setChecked(sd.isClosed()); + if (sd.getOpenTime() != null) tvOpenTime.setText(sd.getOpenTime()); + if (sd.getCloseTime() != null) tvCloseTime.setText(sd.getCloseTime()); + + tvOpenTime.setEnabled(!sd.isClosed()); + tvCloseTime.setEnabled(!sd.isClosed()); + } + } + } + } } } @@ -213,6 +276,17 @@ public class DefinicoesAdminActivity extends AppCompatActivity { updates.put("logoUrl", photoUrl); } + Map scheduleMap = new HashMap<>(); + for (int i = 0; i < dayKeys.length; i++) { + View view = scheduleViews.get(i); + CheckBox cbClosed = view.findViewById(R.id.cbClosed); + TextView tvOpenTime = view.findViewById(R.id.tvOpenTime); + TextView tvCloseTime = view.findViewById(R.id.tvCloseTime); + + scheduleMap.put(dayKeys[i], new ScheduleDay(cbClosed.isChecked(), tvOpenTime.getText().toString(), tvCloseTime.getText().toString())); + } + updates.put("schedule", scheduleMap); + databaseReference.child(documentId).updateChildren(updates) .addOnSuccessListener(aVoid -> { Toast.makeText(this, "Definições guardadas com sucesso!", Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/com/example/pap_teste/EstablishmentDashboardActivity.java b/app/src/main/java/com/example/pap_teste/EstablishmentDashboardActivity.java index f711170..0ea8a6f 100644 --- a/app/src/main/java/com/example/pap_teste/EstablishmentDashboardActivity.java +++ b/app/src/main/java/com/example/pap_teste/EstablishmentDashboardActivity.java @@ -99,6 +99,7 @@ public class EstablishmentDashboardActivity extends AppCompatActivity { loadProximasReservas(); loadEstatisticas(); + checkPendingReservationsOnLogin(); } private void loadProximasReservas() { @@ -247,4 +248,70 @@ public class EstablishmentDashboardActivity extends AppCompatActivity { } }); } + + private void checkPendingReservationsOnLogin() { + String email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL); + if (email == null) { + if (com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null) { + email = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail(); + } else { + return; + } + } + final String finalEmail = email != null ? email.trim() : ""; + + com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas") + .addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() { + @Override + public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) { + java.util.List pendingReservations = new java.util.ArrayList<>(); + 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(finalEmail)) { + if ("Pendente".equals(r.getEstado())) { + pendingReservations.add(r); + } + } + } + + if (!pendingReservations.isEmpty()) { + showPendingReservationsDialog(pendingReservations); + } + } + + @Override + public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) { + } + }); + } + + private void showPendingReservationsDialog(java.util.List pendingReservations) { + if (isFinishing() || isDestroyed()) return; + android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(this); + builder.setTitle("Reservas Pendentes (" + pendingReservations.size() + ")"); + + StringBuilder message = new StringBuilder("Tem reservas a aguardar confirmação:\n\n"); + for (com.example.pap_teste.models.Reserva r : pendingReservations) { + message.append("• ").append(r.getData()).append(" às ").append(r.getHora()) + .append(" - ").append(r.getPessoas()).append(" pessoas") + .append("\n Cliente: ").append(r.getClienteEmail()).append("\n\n"); + } + + builder.setMessage(message.toString()); + builder.setPositiveButton("Ver Reservas", (dialog, which) -> { + Intent intent = new Intent(EstablishmentDashboardActivity.this, DetalhesReservasActivity.class); + intent.putExtra(MainActivity.EXTRA_EMAIL, getIntent().getStringExtra(MainActivity.EXTRA_EMAIL)); + startActivity(intent); + }); + builder.setNegativeButton("Fechar", (dialog, which) -> dialog.dismiss()); + + android.app.AlertDialog dialog = builder.create(); + dialog.show(); + + android.widget.TextView textView = dialog.findViewById(android.R.id.message); + if (textView != null) { + textView.setTextSize(18); + textView.setGravity(android.view.Gravity.CENTER); + } + } } 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 49e0e24..204b128 100644 --- a/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java +++ b/app/src/main/java/com/example/pap_teste/NovaReservaActivity.java @@ -79,6 +79,18 @@ public class NovaReservaActivity extends AppCompatActivity { } else { txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : "")); setupReservationOptions(); + + android.widget.TextView txtFechado = findViewById(R.id.txtRestauranteFechado); + boolean isFechado = selectedRestaurant != null && selectedRestaurant.isFechadoWebsite(); + + if (txtFechado != null) { + txtFechado.setVisibility(isFechado ? android.view.View.VISIBLE : android.view.View.GONE); + } + + findViewById(R.id.btnSelectDate).setEnabled(!isFechado); + findViewById(R.id.btnSelectTime).setEnabled(!isFechado); + findViewById(R.id.etPartySize).setEnabled(!isFechado); + findViewById(R.id.btnConfirmarReserva).setEnabled(!isFechado); } } @@ -127,10 +139,23 @@ public class NovaReservaActivity extends AppCompatActivity { String email = ds.child("email").getValue(String.class); String cat = ds.child("category").getValue(String.class); String logoUrl = ds.child("logoUrl").getValue(String.class); + Boolean fechadoWebsite = ds.child("fechadoWebsite").getValue(Boolean.class); if (name != null && email != null) { - filteredList.add(new com.example.pap_teste.models.Restaurant(name, cat, email, - false, logoUrl)); + com.example.pap_teste.models.Restaurant rest = new com.example.pap_teste.models.Restaurant(name, cat, email, + false, logoUrl); + if (fechadoWebsite != null) { + rest.setFechadoWebsite(fechadoWebsite); + } + if (ds.hasChild("schedule")) { + java.util.Map schMap = new java.util.HashMap<>(); + for (com.google.firebase.database.DataSnapshot schDs : ds.child("schedule").getChildren()) { + com.example.pap_teste.models.ScheduleDay sd = schDs.getValue(com.example.pap_teste.models.ScheduleDay.class); + schMap.put(schDs.getKey(), sd); + } + rest.setSchedule(schMap); + } + filteredList.add(rest); } } } @@ -165,17 +190,70 @@ public class NovaReservaActivity extends AppCompatActivity { 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); + java.util.Calendar selectedCal = java.util.Calendar.getInstance(); + selectedCal.set(year, month, dayOfMonth); + int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK); + String[] dayKeys = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"}; + String dayKey = dayKeys[dayOfWeek - 1]; + + boolean isClosed = false; + if (selectedRestaurant.getSchedule() != null && selectedRestaurant.getSchedule().containsKey(dayKey)) { + com.example.pap_teste.models.ScheduleDay sd = selectedRestaurant.getSchedule().get(dayKey); + if (sd != null && sd.isClosed()) isClosed = true; + } + + if (isClosed) { + android.widget.Toast.makeText(this, "O restaurante encontra-se encerrado na data selecionada.", android.widget.Toast.LENGTH_LONG).show(); + selectedDate = null; + btnDate.setText("Selecionar Data"); + btnTime.setEnabled(false); + } else { + selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year; + btnDate.setText(selectedDate); + btnTime.setEnabled(true); + } }, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH), cal.get(java.util.Calendar.DAY_OF_MONTH)).show(); }); btnTime.setOnClickListener(v -> { + if (selectedDate == null) { + android.widget.Toast.makeText(this, "Por favor, selecione primeiro a data.", android.widget.Toast.LENGTH_SHORT).show(); + return; + } 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); + String chosenTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute); + + boolean isOutOfHours = false; + if (selectedDate != null && selectedRestaurant.getSchedule() != null) { + try { + java.util.Date d = new java.text.SimpleDateFormat("d/M/yyyy", java.util.Locale.getDefault()).parse(selectedDate); + java.util.Calendar selectedCal = java.util.Calendar.getInstance(); + if (d != null) selectedCal.setTime(d); + int dayOfWeek = selectedCal.get(java.util.Calendar.DAY_OF_WEEK); + String[] dayKeys = {"sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"}; + String dayKey = dayKeys[dayOfWeek - 1]; + + com.example.pap_teste.models.ScheduleDay sd = selectedRestaurant.getSchedule().get(dayKey); + if (sd != null && !sd.isClosed()) { + if (sd.getOpenTime() != null && sd.getCloseTime() != null && !sd.getOpenTime().isEmpty() && !sd.getCloseTime().isEmpty()) { + if (chosenTime.compareTo(sd.getOpenTime()) < 0 || chosenTime.compareTo(sd.getCloseTime()) > 0) { + isOutOfHours = true; + } + } + } + } catch (Exception e) {} + } + + if (isOutOfHours) { + android.widget.Toast.makeText(this, "Por favor selecione uma hora dentro do horário de funcionamento.", android.widget.Toast.LENGTH_LONG).show(); + selectedTime = null; + btnTime.setText("Selecionar Hora"); + } else { + selectedTime = chosenTime; + btnTime.setText(selectedTime); + } }, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show(); }); diff --git a/app/src/main/java/com/example/pap_teste/ProfileDashboardActivity.java b/app/src/main/java/com/example/pap_teste/ProfileDashboardActivity.java index 5643888..b00b307 100644 --- a/app/src/main/java/com/example/pap_teste/ProfileDashboardActivity.java +++ b/app/src/main/java/com/example/pap_teste/ProfileDashboardActivity.java @@ -118,6 +118,7 @@ public class ProfileDashboardActivity extends AppCompatActivity { private void performLogOut() { try { + stopService(new Intent(this, ReservationNotificationService.class)); FirebaseAuth.getInstance().signOut(); Toast.makeText(this, "Sessão terminada com sucesso.", Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/com/example/pap_teste/models/Restaurant.java b/app/src/main/java/com/example/pap_teste/models/Restaurant.java index 75786d6..4de9b08 100644 --- a/app/src/main/java/com/example/pap_teste/models/Restaurant.java +++ b/app/src/main/java/com/example/pap_teste/models/Restaurant.java @@ -10,6 +10,7 @@ public class Restaurant implements Serializable { private String logoUrl; private Double ratingAvg; private Integer ratingCount; + private boolean fechadoWebsite; // No-argument constructor required for Firebase public Restaurant() { @@ -41,9 +42,15 @@ public class Restaurant implements Serializable { public void setEmail(String email) { this.email = email; } public void setAvailable(boolean available) { this.available = available; } public void setLogoUrl(String logoUrl) { this.logoUrl = logoUrl; } + public boolean isFechadoWebsite() { return fechadoWebsite; } + public void setFechadoWebsite(boolean fechadoWebsite) { this.fechadoWebsite = fechadoWebsite; } public Double getRatingAvg() { return ratingAvg; } public void setRatingAvg(Double ratingAvg) { this.ratingAvg = ratingAvg; } public Integer getRatingCount() { return ratingCount; } public void setRatingCount(Integer ratingCount) { this.ratingCount = ratingCount; } + + private java.util.Map schedule; + public java.util.Map getSchedule() { return schedule; } + public void setSchedule(java.util.Map schedule) { this.schedule = schedule; } } diff --git a/app/src/main/res/layout/activity_definicoes_admin.xml b/app/src/main/res/layout/activity_definicoes_admin.xml index 74ea0c8..5f9fda5 100644 --- a/app/src/main/res/layout/activity_definicoes_admin.xml +++ b/app/src/main/res/layout/activity_definicoes_admin.xml @@ -156,6 +156,22 @@ android:layout_marginTop="8dp" android:background="@drawable/input_bg" /> + + + + + +