This commit is contained in:
2026-03-10 16:30:08 +00:00
parent d5c457c9a6
commit 7ad72ad334
29 changed files with 1353 additions and 562 deletions

View File

@@ -3,6 +3,9 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
@@ -36,21 +39,13 @@
android:exported="false"
android:label="Lista de espera"
android:theme="@style/Theme.Pap_teste" />
<activity
android:name=".BloqueioHorarioActivity"
android:exported="false"
android:label="Bloqueio de horário"
android:theme="@style/Theme.Pap_teste" />
<activity
android:name=".GestaoStaffActivity"
android:exported="false"
android:label="Gestão de staff"
android:theme="@style/Theme.Pap_teste" />
<activity
android:name=".RelatoriosActivity"
android:exported="false"
android:label="Relatórios"
android:theme="@style/Theme.Pap_teste" />
<activity
android:name=".ClientDashboardActivity"
android:exported="false"
@@ -86,6 +81,16 @@
android:exported="false"
android:label="Conta criada"
android:theme="@style/Theme.Pap_teste" />
<activity
android:name=".DefinicoesAdminActivity"
android:exported="false"
android:label="Definições"
android:theme="@style/Theme.Pap_teste" />
<activity
android:name=".ProfileDashboardActivity"
android:exported="false"
android:label="O meu Perfil"
android:theme="@style/Theme.Pap_teste" />
<activity
android:name=".MainActivity"
android:exported="true">

View File

@@ -1,36 +0,0 @@
package com.example.pap_teste;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import android.widget.Button;
public class BloqueioHorarioActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_bloqueio_horario);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.bloqueioRoot), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
Button back = findViewById(R.id.btnVoltar);
if (back != null) {
back.setOnClickListener(v -> finish());
}
}
}

View File

@@ -1,16 +1,40 @@
package com.example.pap_teste;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import android.widget.Button;
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;
public class CheckInAntecipadoActivity extends AppCompatActivity {
public class CheckInAntecipadoActivity extends AppCompatActivity implements LocationListener {
private TextView txtDistancia, txtStatus;
private Button btnConfirmarChegada;
private LocationManager locationManager;
private DatabaseReference databaseReference;
private double restaurantLat, restaurantLon;
private int securityDistance = 500; // default 500m
private boolean settingsLoaded = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -23,14 +47,114 @@ public class CheckInAntecipadoActivity extends AppCompatActivity {
return insets;
});
txtDistancia = findViewById(R.id.txtDistancia);
txtStatus = findViewById(R.id.txtStatusDistancia);
btnConfirmarChegada = findViewById(R.id.btnConfirmarChegada);
Button back = findViewById(R.id.btnVoltar);
if (back != null) {
back.setOnClickListener(v -> finish());
}
String restaurantEmail = getIntent().getStringExtra("restaurant_email");
if (restaurantEmail != null) {
String restaurantId = restaurantEmail.replace(".", "_").replace("@", "_at_");
loadRestaurantSettings(restaurantId);
} else {
txtStatus.setText("Erro: Restaurante não identificado.");
}
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
startLocationUpdates();
btnConfirmarChegada.setOnClickListener(v -> {
Toast.makeText(this, "Chegada confirmada com sucesso!", Toast.LENGTH_LONG).show();
finish();
});
}
private void loadRestaurantSettings(String restaurantId) {
databaseReference = FirebaseDatabase.getInstance().getReference().child("users").child(restaurantId);
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
if (snapshot.exists()) {
Double lat = snapshot.child("latitude").getValue(Double.class);
Double lon = snapshot.child("longitude").getValue(Double.class);
Integer dist = snapshot.child("securityDistance").getValue(Integer.class);
restaurantLat = lat != null ? lat : 0.0;
restaurantLon = lon != null ? lon : 0.0;
securityDistance = dist != null ? dist : 500;
settingsLoaded = true;
} else {
txtStatus.setText("Aviso: Definições do restaurante não encontradas. Usando valores padrão.");
settingsLoaded = true; // Use defaults
}
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
txtStatus.setText("Erro ao carregar definições do restaurante.");
}
});
}
private void startLocationUpdates() {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
txtStatus.setText("Permissão de localização negada.");
return;
}
try {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 5, this);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 5, this);
} catch (Exception e) {
txtStatus.setText("Erro ao iniciar localização: " + e.getMessage());
}
}
@Override
public void onLocationChanged(@NonNull Location location) {
if (!settingsLoaded)
return;
float[] results = new float[1];
Location.distanceBetween(location.getLatitude(), location.getLongitude(), restaurantLat, restaurantLon,
results);
float distanceInMeters = results[0];
txtDistancia.setText(String.format("Distância: %.0f metros", distanceInMeters));
if (distanceInMeters <= securityDistance) {
txtStatus.setText("Está no raio de segurança. Pode confirmar a sua chegada.");
txtStatus.setTextColor(getResources().getColor(android.R.color.holo_green_dark));
btnConfirmarChegada.setEnabled(true);
} else {
txtStatus.setText(String.format("Está fora do raio de segurança (%dm). Aproxime-se para fazer check-in.",
securityDistance));
txtStatus.setTextColor(getResources().getColor(android.R.color.holo_red_dark));
btnConfirmarChegada.setEnabled(false);
}
}
@Override
protected void onStop() {
super.onStop();
locationManager.removeUpdates(this);
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(@NonNull String provider) {
}
@Override
public void onProviderDisabled(@NonNull String provider) {
}
}

View File

@@ -11,8 +11,17 @@ import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.example.pap_teste.models.FoodCategory;
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<Intent> profileLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -24,57 +33,67 @@ public class ClientDashboardActivity extends AppCompatActivity {
return insets;
});
TextView txtGreeting = findViewById(R.id.txtClientGreeting);
TextView txtStatus = findViewById(R.id.txtClientStatus);
TextView txtRole = findViewById(R.id.txtClientRole);
TextView txtReservationStatus = findViewById(R.id.txtReservationStatus);
TextView txtReservationSubtitle = findViewById(R.id.txtReservationSubtitle);
Button btnBack = findViewById(R.id.btnVoltar);
txtGreeting = findViewById(R.id.txtClientGreeting);
String actionMode = getIntent().getStringExtra(MainActivity.EXTRA_ACTION_MODE);
String displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
String role = getIntent().getStringExtra(MainActivity.EXTRA_ROLE);
email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
role = getIntent().getStringExtra(MainActivity.EXTRA_ROLE);
boolean isNewAccount = "CRIAR".equalsIgnoreCase(actionMode);
txtGreeting.setText(String.format("Olá, %s", displayName != null ? displayName : "convidado"));
txtRole.setText(String.format("Função: %s", role != null ? role : "CLIENTE"));
txtStatus.setText(isNewAccount
? "Conta criada com sucesso! Configure as suas preferências para começarmos."
: "Bom tê-lo de volta! Já deixámos tudo pronto para a sua próxima reserva.");
txtReservationStatus.setText("Próxima reserva");
txtReservationSubtitle.setText("Mesa para 2 • Amanhã às 20h • Sabor & Arte");
Button btnNewReservation = findViewById(R.id.btnNovaReserva);
Button btnExplore = findViewById(R.id.btnExplorar);
Button btnFavorites = findViewById(R.id.btnFavoritos);
Button btnCheckIn = findViewById(R.id.btnCheckIn);
Button btnShare = findViewById(R.id.btnPartilhar);
btnNewReservation.setOnClickListener(v ->
startActivity(new Intent(this, NovaReservaActivity.class))
);
btnExplore.setOnClickListener(v ->
startActivity(new Intent(this, ExplorarRestaurantesActivity.class))
);
btnFavorites.setOnClickListener(v ->
startActivity(new Intent(this, FavoritosActivity.class))
);
btnCheckIn.setOnClickListener(v ->
startActivity(new Intent(this, CheckInAntecipadoActivity.class))
);
btnShare.setOnClickListener(v ->
startActivity(new Intent(this, PartilharReservaActivity.class))
);
if (btnBack != null) {
btnBack.setOnClickListener(v -> finish());
profileLauncher = registerForActivityResult(
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK) {
android.widget.Toast.makeText(this,
"Perfil atualizado. Reinicie para ver mudanças.",
android.widget.Toast.LENGTH_SHORT).show();
}
});
updateGreeting();
setupCategories();
setupActions();
}
private void updateGreeting() {
txtGreeting.setText(String.format("Olá, %s", displayName != null ? displayName : "convidado"));
}
private void setupCategories() {
RecyclerView rv = findViewById(R.id.rvCategories);
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.ic_launcher_background));
cats.add(new FoodCategory("Sobremesas", R.drawable.ic_launcher_background));
FoodCategoryAdapter adapter = new FoodCategoryAdapter(cats);
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.btnCheckIn).setOnClickListener(v -> {
Intent intent = new Intent(this, CheckInAntecipadoActivity.class);
intent.putExtra("restaurant_email", "sabor_arte@restaurante.com");
startActivity(intent);
});
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.btnExplorar).setOnClickListener(
v -> startActivity(new Intent(this, ExplorarRestaurantesActivity.class)));
}
}

View File

@@ -0,0 +1,129 @@
package com.example.pap_teste;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Button;
import android.widget.EditText;
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 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.HashMap;
import java.util.Map;
public class DefinicoesAdminActivity extends AppCompatActivity {
private EditText inputRadius, inputLatitude, inputLongitude;
private DatabaseReference databaseReference;
private String documentId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_definicoes_admin);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.definicoesRoot), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
String email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
if (email != null) {
documentId = email.replace(".", "_").replace("@", "_at_");
}
databaseReference = FirebaseDatabase.getInstance().getReference().child("users");
inputRadius = findViewById(R.id.inputRadius);
inputLatitude = findViewById(R.id.inputLatitude);
inputLongitude = findViewById(R.id.inputLongitude);
Button btnSave = findViewById(R.id.btnSaveSettings);
Button btnBack = findViewById(R.id.btnVoltar);
if (btnBack != null) {
btnBack.setOnClickListener(v -> finish());
}
loadCurrentSettings();
btnSave.setOnClickListener(v -> saveSettings());
}
private void loadCurrentSettings() {
if (documentId == null)
return;
databaseReference.child(documentId).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
if (snapshot.exists()) {
if (snapshot.hasChild("securityDistance")) {
Object val = snapshot.child("securityDistance").getValue();
inputRadius.setText(val != null ? val.toString() : "");
}
if (snapshot.hasChild("latitude")) {
Object val = snapshot.child("latitude").getValue();
inputLatitude.setText(val != null ? val.toString() : "");
}
if (snapshot.hasChild("longitude")) {
Object val = snapshot.child("longitude").getValue();
inputLongitude.setText(val != null ? val.toString() : "");
}
}
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
Toast.makeText(DefinicoesAdminActivity.this, "Erro ao carregar definições.", Toast.LENGTH_SHORT).show();
}
});
}
private void saveSettings() {
if (documentId == null)
return;
String radiusStr = inputRadius.getText().toString().trim();
String latStr = inputLatitude.getText().toString().trim();
String lonStr = inputLongitude.getText().toString().trim();
if (TextUtils.isEmpty(radiusStr) || TextUtils.isEmpty(latStr) || TextUtils.isEmpty(lonStr)) {
Toast.makeText(this, "Preencha todos os campos.", Toast.LENGTH_SHORT).show();
return;
}
try {
int radius = Integer.parseInt(radiusStr);
double lat = Double.parseDouble(latStr);
double lon = Double.parseDouble(lonStr);
Map<String, Object> updates = new HashMap<>();
updates.put("securityDistance", radius);
updates.put("latitude", lat);
updates.put("longitude", lon);
databaseReference.child(documentId).updateChildren(updates)
.addOnSuccessListener(aVoid -> {
Toast.makeText(this, "Definições guardadas com sucesso!", Toast.LENGTH_SHORT).show();
finish();
})
.addOnFailureListener(
e -> Toast.makeText(this, "Falha ao guardar definições.", Toast.LENGTH_SHORT).show());
} catch (NumberFormatException e) {
Toast.makeText(this, "Valores inválidos.", Toast.LENGTH_SHORT).show();
}
}
}

View File

@@ -25,6 +25,7 @@ public class DetalhesReservasActivity extends AppCompatActivity {
private TextView txtNotas;
private TextView txtEstado;
private TextView txtMensagem;
private Button btnConfirmar, btnRecusar, btnApagar;
private int selectedIndex = -1;
@Override
@@ -51,6 +52,10 @@ public class DetalhesReservasActivity extends AppCompatActivity {
txtEstado = findViewById(R.id.txtReservaEstado);
txtMensagem = findViewById(R.id.txtMensagemReserva);
btnConfirmar = findViewById(R.id.btnConfirmarReserva);
btnRecusar = findViewById(R.id.btnCancelarReserva);
btnApagar = findViewById(R.id.btnApagarReserva);
Button back = findViewById(R.id.btnVoltar);
if (back != null) {
back.setOnClickListener(v -> finish());
@@ -61,6 +66,7 @@ public class DetalhesReservasActivity extends AppCompatActivity {
reservas.add(new ReservaItem("Ana Ribeiro", "Mesa 12", "20h00", 4, "Aniversário", "Confirmada"));
reservas.add(new ReservaItem("Bruno Costa", "Mesa 03", "21h15", 2, "Preferência por janela", "Pendente"));
reservas.add(new ReservaItem("Carla Silva", "Mesa 07", "19h30", 3, "Levar bolo para a mesa", "Pendente"));
reservas.add(new ReservaItem("Duarte Neves", "Mesa 01", "22h00", 2, "", "Concluída"));
}
private void setupList() {
@@ -75,16 +81,59 @@ public class DetalhesReservasActivity extends AppCompatActivity {
}
private void setupActions() {
Button btnConfirmar = findViewById(R.id.btnConfirmarReserva);
Button btnCancelar = findViewById(R.id.btnCancelarReserva);
if (btnConfirmar != null) {
btnConfirmar.setOnClickListener(v -> atualizarEstadoSelecionado("Confirmada"));
btnConfirmar.setOnClickListener(v -> {
ReservaItem item = reservas.get(selectedIndex);
if ("Pendente".equals(item.estado)) {
atualizarEstadoSelecionado("Confirmada");
} else if ("Confirmada".equals(item.estado)) {
atualizarEstadoSelecionado("Concluída");
}
});
}
if (btnCancelar != null) {
btnCancelar.setOnClickListener(v -> atualizarEstadoSelecionado("Cancelada"));
if (btnRecusar != null) {
btnRecusar.setOnClickListener(v -> showRecusarDialog());
}
if (btnApagar != null) {
btnApagar.setOnClickListener(v -> apagarReserva());
}
}
private void showRecusarDialog() {
if (selectedIndex < 0)
return;
String[] motivos = { "Sem espaço no restaurante", "Fora de horas", "Reserva duplicada", "Outro" };
new androidx.appcompat.app.AlertDialog.Builder(this)
.setTitle("Motivo da Recusa")
.setItems(motivos, (dialog, which) -> {
atualizarEstadoSelecionado("Recusada (" + motivos[which] + ")");
})
.setNegativeButton("Voltar", null)
.show();
}
private void apagarReserva() {
if (selectedIndex < 0)
return;
new androidx.appcompat.app.AlertDialog.Builder(this)
.setTitle("Apagar Reserva")
.setMessage("Tem a certeza que deseja apagar esta reserva?")
.setPositiveButton("Apagar", (dialog, which) -> {
reservas.remove(selectedIndex);
selectedIndex = -1;
txtInfo.setText("Selecione uma reserva");
txtNotas.setText("");
txtEstado.setText("Estado:");
txtMensagem.setText("Reserva apagada com sucesso.");
toggleButtons(null);
refreshList();
})
.setNegativeButton("Voltar", null)
.show();
}
private void atualizarEstadoSelecionado(String novoEstado) {
@@ -104,6 +153,41 @@ public class DetalhesReservasActivity extends AppCompatActivity {
txtInfo.setText(String.format("%s • %s • %s • %dp", item.nomeCliente, item.mesa, item.hora, item.pessoas));
txtNotas.setText(item.notas);
txtEstado.setText(String.format("Estado: %s", item.estado));
toggleButtons(item);
}
private void toggleButtons(ReservaItem item) {
if (item == null) {
btnConfirmar.setVisibility(android.view.View.GONE);
btnRecusar.setVisibility(android.view.View.GONE);
btnApagar.setVisibility(android.view.View.GONE);
return;
}
switch (item.estado) {
case "Pendente":
btnConfirmar.setText("Confirmar");
btnConfirmar.setVisibility(android.view.View.VISIBLE);
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
break;
}
}
private void refreshList() {
@@ -133,13 +217,3 @@ public class DetalhesReservasActivity extends AppCompatActivity {
}
}
}

View File

@@ -40,40 +40,29 @@ public class EstablishmentDashboardActivity extends AppCompatActivity {
: "Dashboard operacional. Acompanhe as reservas em tempo real.");
Button btnOpenWalkIns = findViewById(R.id.btnAbrirEspera);
Button btnBlockTime = findViewById(R.id.btnCriarBloqueio);
Button btnStaff = findViewById(R.id.btnGestaoStaff);
Button btnReports = findViewById(R.id.btnVerRelatorios);
Button btnGerirMesas = findViewById(R.id.btnGerirMesas);
Button btnDetalhesReservas = findViewById(R.id.btnDetalhesReservas);
Button btnDetails = findViewById(R.id.btnDetalhesReservas);
Button btnSettings = findViewById(R.id.btnDefinicoes);
Button btnBack = findViewById(R.id.btnVoltar);
btnOpenWalkIns.setOnClickListener(v ->
startActivity(new Intent(this, ListaEsperaActivity.class))
);
btnOpenWalkIns.setOnClickListener(v -> startActivity(new Intent(this, ListaEsperaActivity.class)));
btnBlockTime.setOnClickListener(v ->
startActivity(new Intent(this, BloqueioHorarioActivity.class))
);
btnStaff.setOnClickListener(v -> startActivity(new Intent(this, GestaoStaffActivity.class)));
btnStaff.setOnClickListener(v ->
startActivity(new Intent(this, GestaoStaffActivity.class))
);
btnGerirMesas.setOnClickListener(v -> startActivity(new Intent(this, GerirMesasActivity.class)));
btnReports.setOnClickListener(v ->
startActivity(new Intent(this, RelatoriosActivity.class))
);
btnDetails.setOnClickListener(v -> startActivity(new Intent(this, DetalhesReservasActivity.class)));
btnGerirMesas.setOnClickListener(v ->
startActivity(new Intent(this, GerirMesasActivity.class))
);
btnDetalhesReservas.setOnClickListener(v ->
startActivity(new Intent(this, DetalhesReservasActivity.class))
);
btnSettings.setOnClickListener(v -> {
Intent intent = new Intent(this, DefinicoesAdminActivity.class);
intent.putExtra(MainActivity.EXTRA_EMAIL, getIntent().getStringExtra(MainActivity.EXTRA_EMAIL));
startActivity(intent);
});
if (btnBack != null) {
btnBack.setOnClickListener(v -> finish());
}
}
}

View File

@@ -0,0 +1,55 @@
package com.example.pap_teste;
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.FoodCategory;
import java.util.List;
public class FoodCategoryAdapter extends RecyclerView.Adapter<FoodCategoryAdapter.ViewHolder> {
private final List<FoodCategory> categories;
public FoodCategoryAdapter(List<FoodCategory> categories) {
this.categories = categories;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_food_category, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
FoodCategory category = categories.get(position);
holder.txtName.setText(category.getName());
if (category.getImageResId() != 0) {
holder.imgCategory.setImageResource(category.getImageResId());
}
}
@Override
public int getItemCount() {
return categories.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
ImageView imgCategory;
TextView txtName;
public ViewHolder(@NonNull View itemView) {
super(itemView);
imgCategory = itemView.findViewById(R.id.imgCategory);
txtName = itemView.findViewById(R.id.txtCategoryName);
}
}
}

View File

@@ -37,6 +37,7 @@ public class GerirMesasActivity extends AppCompatActivity {
private Spinner spinnerEstado;
private TextView txtMensagem;
private DatabaseReference mDatabase;
private Mesa selectedMesa = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -88,6 +89,8 @@ public class GerirMesasActivity extends AppCompatActivity {
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
Mesa mesa = postSnapshot.getValue(Mesa.class);
if (mesa != null) {
// Ensure the ID is set from the snapshot key if it's missing in the value
mesa.setId(postSnapshot.getKey());
mesas.add(mesa);
String resumo = String.format("Mesa %02d • %d lugares • %s", mesa.getNumero(),
mesa.getCapacidade(), mesa.getEstado());
@@ -106,6 +109,7 @@ public class GerirMesasActivity extends AppCompatActivity {
listMesas.setOnItemClickListener((parent, view, position, id) -> {
Mesa item = mesas.get(position);
selectedMesa = item;
inputNumero.setText(String.valueOf(item.getNumero()));
inputCapacidade.setText(String.valueOf(item.getCapacidade()));
spinnerEstado.setSelection(getEstadoIndex(item.getEstado()));
@@ -128,6 +132,34 @@ public class GerirMesasActivity extends AppCompatActivity {
if (btnGuardar != null) {
btnGuardar.setOnClickListener(v -> guardarMesa());
}
Button btnRemover = findViewById(R.id.btnRemoverMesa);
if (btnRemover != null) {
btnRemover.setOnClickListener(v -> removerMesa());
}
}
private void removerMesa() {
if (selectedMesa == null || selectedMesa.getId() == null) {
Toast.makeText(this, "Selecione uma mesa válida para remover.", Toast.LENGTH_SHORT).show();
return;
}
mDatabase.child(selectedMesa.getId()).removeValue()
.addOnSuccessListener(aVoid -> {
Toast.makeText(this, "Mesa removida com sucesso.", Toast.LENGTH_SHORT).show();
limparCampos();
})
.addOnFailureListener(e -> {
Toast.makeText(this, "Erro ao remover mesa: " + e.getMessage(), Toast.LENGTH_SHORT).show();
});
}
private void limparCampos() {
inputNumero.setText("");
inputCapacidade.setText("");
selectedMesa = null;
txtMensagem.setText("");
}
private void guardarMesa() {
@@ -162,6 +194,10 @@ public class GerirMesasActivity extends AppCompatActivity {
txtMensagem.setText(String.format("Mesa %d adicionada.", numero));
} else {
mesaId = existente.getId();
if (mesaId == null) {
Toast.makeText(this, "Erro ao atualizar: ID não encontrado.", Toast.LENGTH_SHORT).show();
return;
}
existente.setCapacidade(capacidade);
existente.setEstado(estado);
mDatabase.child(mesaId).setValue(existente);
@@ -169,8 +205,7 @@ public class GerirMesasActivity extends AppCompatActivity {
}
// Clearing inputs
inputNumero.setText("");
inputCapacidade.setText("");
limparCampos();
}
private Mesa findMesa(int numero) {

View File

@@ -169,6 +169,14 @@ public class GestaoStaffActivity extends AppCompatActivity {
}
});
}
Button btnGerirMesas = findViewById(R.id.btnGerirMesasStaff);
if (btnGerirMesas != null) {
btnGerirMesas.setOnClickListener(v -> {
Intent intent = new Intent(GestaoStaffActivity.this, GerirMesasActivity.class);
startActivity(intent);
});
}
}
private void guardarAtribuicao() {

View File

@@ -15,6 +15,12 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.content.ContextCompat;
import androidx.appcompat.app.AlertDialog;
import android.Manifest;
import android.content.pm.PackageManager;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import com.google.firebase.FirebaseApp;
import com.google.firebase.auth.FirebaseAuth;
@@ -62,6 +68,22 @@ public class MainActivity extends AppCompatActivity {
private FirebaseAuth firebaseAuth;
private DatabaseReference databaseReference;
private final ActivityResultLauncher<String[]> locationPermissionRequest = registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(), result -> {
Boolean fineLocationGranted = result.getOrDefault(
Manifest.permission.ACCESS_FINE_LOCATION, false);
Boolean coarseLocationGranted = result.getOrDefault(
Manifest.permission.ACCESS_COARSE_LOCATION, false);
if (fineLocationGranted != null && fineLocationGranted) {
// Precise location access granted.
} else if (coarseLocationGranted != null && coarseLocationGranted) {
// Only approximate location access granted.
} else {
Toast.makeText(this, "A permissão de localização é necessária para o check-in.", Toast.LENGTH_LONG)
.show();
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -81,6 +103,28 @@ public class MainActivity extends AppCompatActivity {
setupTypeToggle();
setupActionToggle();
setupPrimaryAction();
checkLocationPermissions();
}
private void checkLocationPermissions() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
new AlertDialog.Builder(this)
.setTitle("Partilha de localização")
.setMessage(
"Para permitir o check-in antecipado, precisamos de saber a sua distância ao restaurante. Deseja permitir a partilha de localização?")
.setPositiveButton("Sim", (dialog, which) -> {
locationPermissionRequest.launch(new String[] {
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
});
})
.setNegativeButton("Não", (dialog, which) -> {
Toast.makeText(this, "A localização foi recusada. O check-in poderá não funcionar.",
Toast.LENGTH_SHORT).show();
})
.show();
}
}
private void bindViews() {
@@ -341,7 +385,8 @@ public class MainActivity extends AppCompatActivity {
DataSnapshot snapshot = task.getResult();
if (snapshot == null || !snapshot.exists()) {
Toast.makeText(this, "Conta sem perfil na cloud. A entrar em modo básico.", Toast.LENGTH_SHORT).show();
// Toast.makeText(this, "Conta sem perfil na cloud. A entrar em modo básico.",
// Toast.LENGTH_SHORT).show();
navigateToDashboard(email, fallbackName, resolvedRole);
return;
}

View File

@@ -0,0 +1,94 @@
package com.example.pap_teste;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import java.util.HashMap;
import java.util.Map;
public class ProfileDashboardActivity extends AppCompatActivity {
private EditText inputName;
private String email, documentId;
private DatabaseReference databaseReference;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_profile_dashboard);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.profileRoot), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
String currentName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
if (email != null) {
documentId = email.replace(".", "_").replace("@", "_at_");
}
databaseReference = FirebaseDatabase.getInstance().getReference().child("users");
inputName = findViewById(R.id.inputProfileName);
if (currentName != null) {
inputName.setText(currentName);
}
Button btnSave = findViewById(R.id.btnSaveProfile);
Button btnBack = findViewById(R.id.btnVoltar);
Button btnFavs = findViewById(R.id.btnFavoritos);
Button btnRes = findViewById(R.id.btnMinhasReservas);
btnBack.setOnClickListener(v -> finish());
btnSave.setOnClickListener(v -> saveProfile());
btnFavs.setOnClickListener(v -> {
Toast.makeText(this, "A abrir Favoritos...", Toast.LENGTH_SHORT).show();
// startActivity(new Intent(this, FavoritosActivity.class));
});
btnRes.setOnClickListener(v -> {
Toast.makeText(this, "A abrir Reservas...", Toast.LENGTH_SHORT).show();
// startActivity(new Intent(this, DetalhesReservasActivity.class));
});
}
private void saveProfile() {
if (documentId == null)
return;
String newName = inputName.getText().toString().trim();
if (TextUtils.isEmpty(newName)) {
Toast.makeText(this, "Indique um nome.", Toast.LENGTH_SHORT).show();
return;
}
Map<String, Object> updates = new HashMap<>();
updates.put("displayName", newName);
databaseReference.child(documentId).updateChildren(updates)
.addOnSuccessListener(aVoid -> {
Toast.makeText(this, "Perfil atualizado!", Toast.LENGTH_SHORT).show();
setResult(RESULT_OK);
finish();
})
.addOnFailureListener(
e -> Toast.makeText(this, "Falha ao atualizar perfil.", Toast.LENGTH_SHORT).show());
}
}

View File

@@ -1,31 +0,0 @@
package com.example.pap_teste;
import android.os.Bundle;
import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import android.widget.Button;
public class RelatoriosActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_relatorios);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.relatoriosRoot), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
Button back = findViewById(R.id.btnVoltar);
if (back != null) {
back.setOnClickListener(v -> finish());
}
}
}

View File

@@ -0,0 +1,19 @@
package com.example.pap_teste.models;
public class FoodCategory {
private String name;
private int imageResId;
public FoodCategory(String name, int imageResId) {
this.name = name;
this.imageResId = imageResId;
}
public String getName() {
return name;
}
public int getImageResId() {
return imageResId;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 485 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

View File

@@ -1,53 +0,0 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/bloqueioRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F7F7F7"
tools:context=".BloqueioHorarioActivity">
<Button
android:id="@+id/btnVoltar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Voltar"
android:textAllCaps="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txtTituloBloqueio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Bloqueio de horário"
android:textSize="22sp"
android:textStyle="bold"
android:textColor="#000"
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="32dp" />
<TextView
android:id="@+id/txtDescricaoBloqueio"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="12dp"
android:text="Neste ecrã o estabelecimento poderá marcar períodos em que não aceita reservas."
android:textSize="14sp"
android:textColor="#4D4D4D"
app:layout_constraintTop_toBottomOf="@id/txtTituloBloqueio"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -38,13 +38,55 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="12dp"
android:text="Aqui o cliente poderá confirmar a chegada antes do horário marcado."
android:text="Aqui poderá confirmar a sua chegada ao restaurante."
android:textSize="14sp"
android:textColor="#4D4D4D"
app:layout_constraintTop_toBottomOf="@id/txtTituloCheckin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/txtDistancia"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="A calcular distância..."
android:textSize="18sp"
android:textColor="#2E7D32"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/txtDescricaoCheckin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<TextView
android:id="@+id/txtStatusDistancia"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
android:layout_marginTop="8dp"
android:text="Por favor, aguarde enquanto validamos a sua localização."
android:textAlignment="center"
android:textSize="14sp"
android:textColor="#5F5F5F"
app:layout_constraintTop_toBottomOf="@id/txtDistancia"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<Button
android:id="@+id/btnConfirmarChegada"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
android:layout_marginTop="32dp"
android:text="Confirmar Chegada"
android:textAllCaps="false"
android:enabled="false"
app:layout_constraintTop_toBottomOf="@id/txtStatusDistancia"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -5,273 +5,287 @@
android:id="@+id/clientRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F7F7F7"
android:background="#FFFFFF"
tools:context=".ClientDashboardActivity">
<Button
android:id="@+id/btnVoltar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Voltar"
android:textAllCaps="false"
<!-- Top Bar with Profile Icon -->
<androidx.cardview.widget.CardView
android:id="@+id/cardProfile"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
app:cardCornerRadius="22.5dp"
app:cardElevation="2dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent">
<ScrollView
android:id="@+id/clientScroll"
android:layout_width="0dp"
android:layout_height="0dp"
android:fillViewport="true"
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<LinearLayout
<ImageView
android:id="@+id/imgProfile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/circle_bg"
android:padding="8dp" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/txtClientGreeting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:text="Olá, convidado"
android:textColor="#000"
android:textSize="24sp"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/cardProfile"
app:layout_constraintStart_toEndOf="@id/cardProfile"
app:layout_constraintTop_toTopOf="@id/cardProfile" />
<Button
android:id="@+id/btnVoltar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="Sair"
android:textAllCaps="false"
android:background="@android:color/transparent"
android:textColor="#FF5252"
app:layout_constraintBottom_toBottomOf="@id/cardProfile"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/cardProfile" />
<ScrollView
android:id="@+id/clientScroll"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:fillViewport="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/cardProfile">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="24dp">
<!-- Categories Section -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:text="Categorias"
android:textColor="#000"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvCategories"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:clipToPadding="false"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_food_category" />
<!-- Integrated Next Reservation Card -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:text="A sua próxima reserva"
android:textColor="#000"
android:textSize="18sp"
android:textStyle="bold" />
<androidx.cardview.widget.CardView
android:id="@+id/cardNextReservation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="12dp"
app:cardBackgroundColor="#F8F9FA"
app:cardCornerRadius="20dp"
app:cardElevation="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/txtResTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sabor &amp; Arte"
android:textColor="#000"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txtResTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/txtResTitle"
android:layout_marginTop="4dp"
android:text="Amanhã às 20h00 • 2 pessoas"
android:textColor="#616161"
android:textSize="14sp" />
<ImageView
android:id="@+id/imgResIcon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignParentEnd="true"
android:src="@drawable/circle_bg"
app:tint="#0066CC" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="16dp"
android:background="#E0E0E0" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnCheckIn"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_marginEnd="6dp"
android:layout_weight="1"
android:background="@drawable/btn_primary"
android:text="Check-in"
android:textAllCaps="false"
android:textColor="#FFF"
android:textSize="14sp" />
<Button
android:id="@+id/btnPartilhar"
android:layout_width="0dp"
android:layout_height="45dp"
android:layout_marginStart="6dp"
android:layout_weight="1"
android:background="@drawable/btn_light_border"
android:text="Partilhar"
android:textAllCaps="false"
android:textColor="#000"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Actions Section -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="32dp"
android:text="Mais opções"
android:textColor="#000"
android:textSize="18sp"
android:textStyle="bold" />
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginTop="12dp"
android:columnCount="2"
android:rowCount="1">
<androidx.cardview.widget.CardView
android:id="@+id/cardNewRes"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="2dp">
<LinearLayout
android:id="@+id/btnNovaReserva"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:background="?attr/selectableItemBackground">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reservar"
android:textColor="#000"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/cardExplore"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_columnWeight="1"
android:layout_margin="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="2dp">
<LinearLayout
android:id="@+id/btnExplorar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:background="?attr/selectableItemBackground">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Explorar"
android:textColor="#000"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</GridLayout>
<!-- Status Section -->
<TextView
android:id="@+id/txtClientStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Conta criada com sucesso!"
android:textColor="#4D4D4D"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="24dp"
android:text="Tudo pronto para a sua próxima refeição!"
android:textAlignment="center"
android:textColor="#757575"
android:textSize="14sp" />
<TextView
android:id="@+id/txtClientRole"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="Função: CLIENTE"
android:textColor="#0066CC"
android:textSize="13sp"
android:textStyle="bold" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:cardBackgroundColor="#FFFFFF"
app:cardCornerRadius="16dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:id="@+id/txtReservationStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Próxima reserva"
android:textColor="#000"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/txtReservationSubtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Mesa para 2 • Amanhã às 20h"
android:textColor="#4D4D4D"
android:textSize="14sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:background="@drawable/circle_bg"
android:padding="12dp"
android:src="@drawable/ic_launcher_foreground" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Restaurante"
android:textColor="#8A8A8A"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sabor &amp; Arte"
android:textColor="#000"
android:textSize="16sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/btnCheckIn"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="20dp"
android:background="@drawable/btn_light"
android:text="Check-in antecipado"
android:textAllCaps="false"
android:textColor="#00001A" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="Ações rápidas"
android:textColor="#000"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:gravity="center"
android:orientation="horizontal">
<Button
android:id="@+id/btnNovaReserva"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:background="@drawable/btn_light_border"
android:text="Nova reserva"
android:textAllCaps="false"
android:textColor="#00001A" />
<Button
android:id="@+id/btnExplorar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:background="@drawable/btn_light_border"
android:text="Explorar"
android:textAllCaps="false"
android:textColor="#00001A" />
<Button
android:id="@+id/btnFavoritos"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:background="@drawable/btn_light_border"
android:text="Favoritos"
android:textAllCaps="false"
android:textColor="#00001A" />
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:cardBackgroundColor="#FFFFFF"
app:cardCornerRadius="16dp"
app:cardElevation="3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Sugestões para hoje"
android:textColor="#000"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="8dp"
android:text="• Rooftop 28 - Disponível às 19h\n• Cantinho do Chef - Disponível às 21h"
android:textColor="#4D4D4D"
android:textSize="14sp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:cardBackgroundColor="#FFFFFF"
app:cardCornerRadius="16dp"
app:cardElevation="3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Partilhe a sua reserva"
android:textColor="#000"
android:textSize="16sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="Envie um convite para os seus convidados."
android:textColor="#4D4D4D"
android:textSize="14sp" />
<Button
android:id="@+id/btnPartilhar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/btn_light"
android:text="Partilhar"
android:textAllCaps="false"
android:textColor="#00001A" />
</LinearLayout>
</androidx.cardview.widget.CardView>
android:text="Modo Cliente"
android:textColor="#BDBDBD"
android:textSize="12sp" />
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,131 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/definicoesRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F2F2F2"
tools:context=".DefinicoesAdminActivity">
<Button
android:id="@+id/btnVoltar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Voltar"
android:textAllCaps="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
android:fillViewport="true"
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Definições do Estabelecimento"
android:textColor="#000"
android:textSize="24sp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="Configure o raio de segurança para check-in antecipado."
android:textColor="#5F5F5F"
android:textSize="14sp" />
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
app:cardBackgroundColor="#FFFFFF"
app:cardCornerRadius="16dp"
app:cardElevation="3dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Raio de Segurança (metros)"
android:textColor="#000"
android:textSize="16sp"
android:textStyle="bold" />
<EditText
android:id="@+id/inputRadius"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="Ex: 500"
android:inputType="number"
android:padding="12dp"
android:background="@android:drawable/editbox_background_normal" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:text="Localização do Restaurante"
android:textColor="#000"
android:textSize="16sp"
android:textStyle="bold" />
<EditText
android:id="@+id/inputLatitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="Latitude (Ex: 38.7223)"
android:inputType="numberDecimal|numberSigned"
android:padding="12dp"
android:background="@android:drawable/editbox_background_normal" />
<EditText
android:id="@+id/inputLongitude"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:hint="Longitude (Ex: -9.1393)"
android:inputType="numberDecimal|numberSigned"
android:padding="12dp"
android:background="@android:drawable/editbox_background_normal" />
<Button
android:id="@+id/btnSaveSettings"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:background="@drawable/btn_light"
android:text="Guardar Definições"
android:textAllCaps="false"
android:textColor="#00001A" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -125,7 +125,7 @@
android:id="@+id/btnConfirmarReserva"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginEnd="4dp"
android:layout_weight="1"
android:background="@drawable/btn_primary"
android:text="Confirmar"
@@ -136,12 +136,24 @@
android:id="@+id/btnCancelarReserva"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginHorizontal="4dp"
android:layout_weight="1"
android:background="@drawable/btn_light_border"
android:text="Cancelar"
android:text="Recusar"
android:textAllCaps="false"
android:textColor="#00001A" />
<Button
android:id="@+id/btnApagarReserva"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_weight="1"
android:background="@drawable/btn_light_border"
android:text="Apagar"
android:textAllCaps="false"
android:textColor="#C62828"
android:visibility="gone" />
</LinearLayout>
<TextView

View File

@@ -281,15 +281,7 @@
android:textAllCaps="false"
android:textColor="#00001A" />
<Button
android:id="@+id/btnCriarBloqueio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:background="@drawable/btn_light_border"
android:text="Criar bloqueio de horário"
android:textAllCaps="false"
android:textColor="#00001A" />
<Button
android:id="@+id/btnGestaoStaff"
@@ -302,13 +294,17 @@
android:textColor="#00001A" />
<Button
android:id="@+id/btnVerRelatorios"
android:id="@+id/btnDefinicoes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:background="@drawable/btn_light_border"
android:text="Ver relatórios"
android:text="Definições"
android:textAllCaps="false"
android:textColor="#00001A" />
</LinearLayout>
<androidx.cardview.widget.CardView

View File

@@ -103,6 +103,16 @@
android:textAllCaps="false"
android:textColor="#FFFFFF" />
<Button
android:id="@+id/btnRemoverMesa"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_marginTop="8dp"
android:background="@drawable/btn_light_border"
android:text="Remover"
android:textAllCaps="false"
android:textColor="#D32F2F" />
<TextView
android:id="@+id/txtMensagemMesa"
android:layout_width="match_parent"

View File

@@ -115,6 +115,21 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/formStaff" />
<Button
android:id="@+id/btnGerirMesasStaff"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:layout_marginEnd="16dp"
android:background="@drawable/btn_light_border"
android:paddingHorizontal="12dp"
android:text="Gerir Mesas"
android:textAllCaps="false"
android:textColor="#00001A"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@id/txtListaStaffTitulo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/txtListaStaffTitulo" />
<ListView
android:id="@+id/listStaffMesas"
android:layout_width="0dp"

View File

@@ -0,0 +1,113 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/profileRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
tools:context=".ProfileDashboardActivity">
<Button
android:id="@+id/btnVoltar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Voltar"
android:textAllCaps="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txtProfileTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="O seu Perfil"
android:textColor="#000"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="20dp" />
<androidx.cardview.widget.CardView
android:id="@+id/cardProfileBig"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="32dp"
app:cardCornerRadius="50dp"
app:cardElevation="4dp"
app:layout_constraintTop_toBottomOf="@id/txtProfileTitle"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/circle_bg"
android:padding="20dp" />
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp"
app:layout_constraintTop_toBottomOf="@id/cardProfileBig">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nome de Exibição"
android:textColor="#8A8A8A"
android:textSize="12sp" />
<EditText
android:id="@+id/inputProfileName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:hint="O seu nome"
android:padding="12dp"
android:background="@drawable/input_bg" />
<Button
android:id="@+id/btnSaveProfile"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@drawable/btn_primary"
android:text="Guardar Alterações"
android:textAllCaps="false"
android:textColor="#FFF" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginVertical="32dp"
android:background="#F0F0F0" />
<Button
android:id="@+id/btnFavoritos"
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="@drawable/btn_light_border"
android:text="Os meus Favoritos"
android:textAllCaps="false"
android:textColor="#000"
android:layout_marginBottom="12dp" />
<Button
android:id="@+id/btnMinhasReservas"
android:layout_width="match_parent"
android:layout_height="55dp"
android:background="@drawable/btn_light_border"
android:text="Histórico de Reservas"
android:textAllCaps="false"
android:textColor="#000" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,53 +0,0 @@
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relatoriosRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F7F7F7"
tools:context=".RelatoriosActivity">
<Button
android:id="@+id/btnVoltar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Voltar"
android:textAllCaps="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/txtTituloRelatorios"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Relatórios"
android:textSize="22sp"
android:textStyle="bold"
android:textColor="#000"
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="32dp" />
<TextView
android:id="@+id/txtDescricaoRelatorios"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="12dp"
android:text="Visualização de relatórios diários, semanais e mensais do desempenho do restaurante."
android:textSize="14sp"
android:textColor="#4D4D4D"
app:layout_constraintTop_toBottomOf="@id/txtTituloRelatorios"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="120dp"
android:layout_height="140dp"
android:layout_margin="8dp"
app:cardCornerRadius="16dp"
app:cardElevation="2dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:padding="8dp">
<ImageView
android:id="@+id/imgCategory"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_launcher_background" />
<TextView
android:id="@+id/txtCategoryName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Categoria"
android:textColor="#000"
android:textSize="14sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.cardview.widget.CardView>

View File

@@ -1,6 +1,7 @@
#Tue Jan 20 14:11:23 WET 2026
#Tue Feb 24 17:05:40 WET 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
distributionSha256Sum=a17ddd85a26b6a7f5ddb71ff8b05fc5104c0202c6e64782429790c933686c806
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists