diff --git a/app/src/main/java/com/example/pap_teste/DetalhesReservasActivity.java b/app/src/main/java/com/example/pap_teste/DetalhesReservasActivity.java index bd2b8a8..d1fd1a2 100644 --- a/app/src/main/java/com/example/pap_teste/DetalhesReservasActivity.java +++ b/app/src/main/java/com/example/pap_teste/DetalhesReservasActivity.java @@ -118,9 +118,9 @@ public class DetalhesReservasActivity extends AppCompatActivity { btnConfirmar.setOnClickListener(v -> { com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex); if ("Pendente".equals(item.getEstado())) { - atualizarEstadoSelecionado("Confirmada"); - } else if ("Confirmada".equals(item.getEstado())) { - atualizarEstadoSelecionado("Concluída"); + mostrarMesasDisponiveis(); + } else if ("Confirmada".equals(item.getEstado()) || item.getEstado().startsWith("Confirmada (Mesa")) { + showConcluirDialog(); } }); } @@ -134,6 +134,97 @@ public class DetalhesReservasActivity extends AppCompatActivity { } } + private void mostrarMesasDisponiveis() { + if (selectedIndex < 0 || selectedIndex >= reservas.size()) { + Toast.makeText(this, "Selecione uma reserva.", Toast.LENGTH_SHORT).show(); + return; + } + com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex); + + com.google.firebase.database.DatabaseReference mesasRef = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Mesas"); + mesasRef.orderByChild("restauranteEmail").equalTo(restaurantEmail).addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() { + @Override + public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) { + List mesasLivres = new ArrayList<>(); + 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.getEstado() != null && m.getEstado().equalsIgnoreCase("Livre") && m.getCapacidade() >= item.getPessoas()) { + m.setId(ds.getKey()); + mesasLivres.add(m); + } + } + + if (mesasLivres.isEmpty()) { + new androidx.appcompat.app.AlertDialog.Builder(DetalhesReservasActivity.this) + .setTitle("Sem mesas disponíveis") + .setMessage("Não há mesas livres com capacidade suficiente para " + item.getPessoas() + " pessoas. Deseja confirmar a reserva mesmo assim (sem mesa atribuída)?") + .setPositiveButton("Sim", (dialog, which) -> atualizarEstadoSelecionado("Confirmada", null)) + .setNegativeButton("Não", null) + .show(); + return; + } + + String[] mesaOptions = new String[mesasLivres.size()]; + for (int i = 0; i < mesasLivres.size(); i++) { + com.example.pap_teste.models.Mesa m = mesasLivres.get(i); + mesaOptions[i] = String.format("Mesa %d (%d lugares)", m.getNumero(), m.getCapacidade()); + } + + new androidx.appcompat.app.AlertDialog.Builder(DetalhesReservasActivity.this) + .setTitle("Atribuir Mesa") + .setItems(mesaOptions, (dialog, which) -> { + com.example.pap_teste.models.Mesa selecionada = mesasLivres.get(which); + confirmarReservaComMesa(item, selecionada); + }) + .setNegativeButton("Cancelar", null) + .show(); + } + + @Override + public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) { + Toast.makeText(DetalhesReservasActivity.this, "Erro ao carregar mesas.", Toast.LENGTH_SHORT).show(); + } + }); + } + + private void confirmarReservaComMesa(com.example.pap_teste.models.Reserva reserva, com.example.pap_teste.models.Mesa mesa) { + String novoEstado = "Confirmada (Mesa " + mesa.getNumero() + ")"; + + java.util.Map updates = new java.util.HashMap<>(); + updates.put("estado", novoEstado); + updates.put("motivo", null); // limpar possível motivo antigo + + databaseReference.child(reserva.getId()).updateChildren(updates).addOnCompleteListener(task -> { + if (task.isSuccessful()) { + com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Mesas").child(mesa.getId()).child("estado").setValue("Reservada") + .addOnCompleteListener(t2 -> { + Toast.makeText(this, "Reserva aceite e mesa atribuída com sucesso.", Toast.LENGTH_SHORT).show(); + reserva.setEstado(novoEstado); + reserva.setMotivo(null); + mostrarDetalhe(reserva); + }); + } else { + Toast.makeText(this, "Erro ao confirmar reserva.", Toast.LENGTH_SHORT).show(); + } + }); + } + + private void showConcluirDialog() { + if (selectedIndex < 0) return; + android.widget.EditText input = new android.widget.EditText(this); + input.setHint("Notas de conclusão (opcional)"); + new androidx.appcompat.app.AlertDialog.Builder(this) + .setTitle("Concluir Reserva") + .setMessage("Deseja adicionar alguma nota ao concluir esta reserva?") + .setView(input) + .setPositiveButton("Concluir", (dialog, which) -> { + String motivo = input.getText().toString(); + atualizarEstadoSelecionado("Concluída", motivo.isEmpty() ? null : motivo); + }) + .setNegativeButton("Cancelar", null) + .show(); + } + private void showRecusarDialog() { if (selectedIndex < 0) return; @@ -142,7 +233,7 @@ public class DetalhesReservasActivity extends AppCompatActivity { new androidx.appcompat.app.AlertDialog.Builder(this) .setTitle("Motivo da Recusa") .setItems(motivos, (dialog, which) -> { - atualizarEstadoSelecionado("Recusada (" + motivos[which] + ")"); + atualizarEstadoSelecionado("Recusada", motivos[which]); }) .setNegativeButton("Voltar", null) .show(); @@ -186,17 +277,27 @@ public class DetalhesReservasActivity extends AppCompatActivity { toggleButtons(null); } - private void atualizarEstadoSelecionado(String novoEstado) { + private void atualizarEstadoSelecionado(String novoEstado, String motivo) { if (selectedIndex < 0 || selectedIndex >= reservas.size()) { Toast.makeText(this, "Selecione uma reserva para atualizar.", Toast.LENGTH_SHORT).show(); return; } com.example.pap_teste.models.Reserva item = reservas.get(selectedIndex); - databaseReference.child(item.getId()).child("estado").setValue(novoEstado).addOnCompleteListener(task -> { + java.util.Map updates = new java.util.HashMap<>(); + updates.put("estado", novoEstado); + if (motivo != null && !motivo.isEmpty()) { + updates.put("motivo", motivo); + } else { + updates.put("motivo", null); + } + + databaseReference.child(item.getId()).updateChildren(updates).addOnCompleteListener(task -> { if (task.isSuccessful()) { txtMensagem .setText(String.format("Reserva de %s marcada como %s.", item.getClienteEmail(), novoEstado)); + item.setEstado(novoEstado); + item.setMotivo(motivo); mostrarDetalhe(item); } }); @@ -206,7 +307,13 @@ public class DetalhesReservasActivity extends AppCompatActivity { txtInfo.setText(String.format("%s • %s • %s • %dp", item.getClienteEmail(), item.getRestauranteName(), item.getHora(), item.getPessoas())); txtNotas.setText("Data: " + item.getData()); - txtEstado.setText(String.format("Estado: %s", item.getEstado())); + + String estadoTexto = item.getEstado(); + if (item.getMotivo() != null && !item.getMotivo().isEmpty()) { + estadoTexto += " (" + item.getMotivo() + ")"; + } + txtEstado.setText(String.format("Estado: %s", estadoTexto)); + toggleButtons(item); } @@ -225,21 +332,23 @@ public class DetalhesReservasActivity extends AppCompatActivity { btnRecusar.setVisibility(android.view.View.VISIBLE); btnApagar.setVisibility(android.view.View.GONE); break; - case "Confirmada": - btnConfirmar.setText("Concluir"); - btnConfirmar.setVisibility(android.view.View.VISIBLE); - btnRecusar.setVisibility(android.view.View.VISIBLE); // Still allow refusal - btnApagar.setVisibility(android.view.View.GONE); - break; case "Concluída": btnConfirmar.setVisibility(android.view.View.GONE); btnRecusar.setVisibility(android.view.View.GONE); btnApagar.setVisibility(android.view.View.VISIBLE); break; - default: // Recusada or Cancelada - btnConfirmar.setVisibility(android.view.View.GONE); - btnRecusar.setVisibility(android.view.View.GONE); - btnApagar.setVisibility(android.view.View.VISIBLE); // Allow deleting refused ones as well + default: + if (item.getEstado() != null && item.getEstado().startsWith("Confirmada")) { + btnConfirmar.setText("Concluir"); + btnConfirmar.setVisibility(android.view.View.VISIBLE); + btnRecusar.setVisibility(android.view.View.VISIBLE); // Still allow refusal + btnApagar.setVisibility(android.view.View.GONE); + } else { + // Recusada or Cancelada + btnConfirmar.setVisibility(android.view.View.GONE); + btnRecusar.setVisibility(android.view.View.GONE); + btnApagar.setVisibility(android.view.View.VISIBLE); // Allow deleting refused ones as well + } break; } } diff --git a/app/src/main/java/com/example/pap_teste/ListaEsperaActivity.java b/app/src/main/java/com/example/pap_teste/ListaEsperaActivity.java index 4e4cf27..0bc40c2 100644 --- a/app/src/main/java/com/example/pap_teste/ListaEsperaActivity.java +++ b/app/src/main/java/com/example/pap_teste/ListaEsperaActivity.java @@ -105,7 +105,7 @@ public class ListaEsperaActivity extends AppCompatActivity { List mesasLivres = new ArrayList<>(); for (DataSnapshot ds : snapshot.getChildren()) { com.example.pap_teste.models.Mesa m = ds.getValue(com.example.pap_teste.models.Mesa.class); - if (m != null && m.getEstado() != null && m.getEstado().equalsIgnoreCase("Livre")) { + if (m != null && m.getEstado() != null && m.getEstado().equalsIgnoreCase("Livre") && m.getCapacidade() >= item.getPessoas()) { m.setId(ds.getKey()); mesasLivres.add(m); } @@ -114,7 +114,7 @@ public class ListaEsperaActivity extends AppCompatActivity { if (mesasLivres.isEmpty()) { new androidx.appcompat.app.AlertDialog.Builder(ListaEsperaActivity.this) .setTitle("Sem mesas disponíveis") - .setMessage("Não há mesas livres registadas. Deseja confirmar a reserva mesmo assim (sem lugar reservado)?") + .setMessage("Não há mesas livres com capacidade suficiente para " + item.getPessoas() + " pessoas. Deseja confirmar a reserva mesmo assim (sem lugar reservado)?") .setPositiveButton("Sim", (dialog, which) -> atualizarEstadoSelecionado("Confirmada")) .setNegativeButton("Não", null) .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 4c2fdec..34ce4fb 100644 --- a/app/src/main/java/com/example/pap_teste/MinhasReservasActivity.java +++ b/app/src/main/java/com/example/pap_teste/MinhasReservasActivity.java @@ -120,13 +120,29 @@ public class MinhasReservasActivity extends AppCompatActivity { } private void cancelReservation(Reserva reserva) { - databaseReference.child(reserva.getId()).child("estado").setValue("Cancelada") - .addOnCompleteListener(task -> { - if (task.isSuccessful()) { - Toast.makeText(this, "Reserva cancelada.", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(this, "Erro ao cancelar reserva.", Toast.LENGTH_SHORT).show(); + android.widget.EditText input = new android.widget.EditText(this); + input.setHint("Motivo do cancelamento (opcional)"); + new androidx.appcompat.app.AlertDialog.Builder(this) + .setTitle("Cancelar Reserva") + .setMessage("Deseja indicar o motivo do cancelamento?") + .setView(input) + .setPositiveButton("Confirmar", (dialog, which) -> { + String motivo = input.getText().toString(); + java.util.Map updates = new java.util.HashMap<>(); + updates.put("estado", "Cancelada"); + if (!motivo.isEmpty()) { + updates.put("motivo", motivo); } - }); + databaseReference.child(reserva.getId()).updateChildren(updates) + .addOnCompleteListener(task -> { + if (task.isSuccessful()) { + Toast.makeText(this, "Reserva cancelada.", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, "Erro ao cancelar reserva.", Toast.LENGTH_SHORT).show(); + } + }); + }) + .setNegativeButton("Voltar", null) + .show(); } } diff --git a/app/src/main/java/com/example/pap_teste/ReservationNotificationService.java b/app/src/main/java/com/example/pap_teste/ReservationNotificationService.java index 55c3e9c..a43929a 100644 --- a/app/src/main/java/com/example/pap_teste/ReservationNotificationService.java +++ b/app/src/main/java/com/example/pap_teste/ReservationNotificationService.java @@ -64,7 +64,7 @@ public class ReservationNotificationService extends Service { Reserva reserva = snapshot.getValue(Reserva.class); if (reserva != null && currentUserEmail.equals(reserva.getClienteEmail())) { String estado = reserva.getEstado(); - if (estado != null && (estado.startsWith("Confirmada") || estado.equals("Recusada"))) { + if (estado != null && (estado.startsWith("Confirmada") || estado.equals("Recusada") || estado.equals("Concluída") || estado.equals("Cancelada"))) { sendNotification(reserva); } } @@ -95,11 +95,16 @@ public class ReservationNotificationService extends Service { String title = "Atualização de Reserva"; String message = "A sua reserva no " + reserva.getRestauranteName() + " foi " + reserva.getEstado().toLowerCase() + "."; + + if (reserva.getMotivo() != null && !reserva.getMotivo().isEmpty()) { + message += " Motivo: " + reserva.getMotivo(); + } NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.na_mesa) // Assuming na_mesa is a valid drawable .setContentTitle(title) .setContentText(message) + .setStyle(new NotificationCompat.BigTextStyle().bigText(message)) .setPriority(NotificationCompat.PRIORITY_HIGH) .setContentIntent(pendingIntent) .setAutoCancel(true); diff --git a/app/src/main/java/com/example/pap_teste/models/Reserva.java b/app/src/main/java/com/example/pap_teste/models/Reserva.java index be20023..a5d037e 100644 --- a/app/src/main/java/com/example/pap_teste/models/Reserva.java +++ b/app/src/main/java/com/example/pap_teste/models/Reserva.java @@ -9,6 +9,7 @@ public class Reserva { private String hora; private int pessoas; private String estado; // Pendente, Confirmada, Concluída, Cancelada, Recusada + private String motivo; public Reserva() { // Required for Firebase @@ -89,4 +90,12 @@ public class Reserva { public void setEstado(String estado) { this.estado = estado; } + + public String getMotivo() { + return motivo; + } + + public void setMotivo(String motivo) { + this.motivo = motivo; + } }