Compare commits
8 Commits
31a7cbb2df
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a8dce01f58 | |||
| f245958b3a | |||
| 4d337426db | |||
| 2e7f520b6d | |||
| 3d817d0b81 | |||
| a02aecc3c9 | |||
| d5fe1a10be | |||
| 5226b032b0 |
@@ -10,7 +10,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.example.pap_teste"
|
applicationId = "com.example.pap_teste"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 36
|
targetSdk = 35
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
||||||
@@ -44,6 +44,11 @@ dependencies {
|
|||||||
implementation(libs.firebase.database)
|
implementation(libs.firebase.database)
|
||||||
implementation(libs.glide)
|
implementation(libs.glide)
|
||||||
implementation(libs.firebase.storage)
|
implementation(libs.firebase.storage)
|
||||||
|
|
||||||
|
// Room
|
||||||
|
implementation(libs.room.runtime)
|
||||||
|
annotationProcessor(libs.room.compiler)
|
||||||
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(libs.ext.junit)
|
androidTestImplementation(libs.ext.junit)
|
||||||
androidTestImplementation(libs.espresso.core)
|
androidTestImplementation(libs.espresso.core)
|
||||||
|
|||||||
@@ -24,95 +24,72 @@
|
|||||||
android:roundIcon="@drawable/na_mesa"
|
android:roundIcon="@drawable/na_mesa"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.Pap_teste">
|
android:theme="@style/Theme.Pap_teste">
|
||||||
<activity
|
|
||||||
android:name=".AddStaffActivity"
|
|
||||||
android:exported="false" />
|
|
||||||
<activity
|
|
||||||
android:name=".DetalhesReservasActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Detalhes das reservas"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".GerirMesasActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Editar mesas"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".EstablishmentDashboardActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Área do Estabelecimento"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".ListaEsperaActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Lista de espera"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".GestaoStaffActivity"
|
android:name=".SplashActivity"
|
||||||
android:exported="false"
|
|
||||||
android:label="Gestão de staff"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".ClientDashboardActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Área do Cliente"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".NovaReservaActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Nova reserva"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".ExplorarRestaurantesActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Explorar restaurantes"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".FavoritosActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Favoritos"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".CheckInAntecipadoActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Check-in antecipado"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".PartilharReservaActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Partilhar reserva"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".AccountCreatedActivity"
|
|
||||||
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=".MinhasReservasActivity"
|
|
||||||
android:exported="false"
|
|
||||||
android:label="Minhas Reservas"
|
|
||||||
android:theme="@style/Theme.Pap_teste" />
|
|
||||||
<activity
|
|
||||||
android:name=".MainActivity"
|
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".OnboardingActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ClientDashboardActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".RestaurantDetailActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ReservationActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".FavoritosActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".EstablishmentDashboardActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".AccountCreatedActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".NovaReservaActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".CheckInAntecipadoActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".MinhasReservasActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".PartilharReservaActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ProfileDashboardActivity"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ExplorarRestaurantesActivity"
|
||||||
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
package com.example.pap_teste;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Toast;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.text.InputType;
|
|
||||||
|
|
||||||
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.example.pap_teste.models.Staff;
|
|
||||||
import com.google.firebase.database.DatabaseReference;
|
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
|
||||||
|
|
||||||
public class AddStaffActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
private Button addButton;
|
|
||||||
private EditText nameEditText;
|
|
||||||
private Spinner zonaSpinner;
|
|
||||||
private Button btnAddZone;
|
|
||||||
private Spinner mesaSpinner;
|
|
||||||
private ArrayList<String> zones;
|
|
||||||
private ArrayList<String> mesas;
|
|
||||||
private ArrayAdapter<String> adapter;
|
|
||||||
private ArrayAdapter<String> mesaAdapter;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
EdgeToEdge.enable(this);
|
|
||||||
setContentView(R.layout.activity_add_staff);
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
|
||||||
return insets;
|
|
||||||
});
|
|
||||||
|
|
||||||
addButton = findViewById(R.id.addButton);
|
|
||||||
nameEditText = findViewById(R.id.nammeEditText);
|
|
||||||
zonaSpinner = findViewById(R.id.zonaSpinner);
|
|
||||||
btnAddZone = findViewById(R.id.btnAddZone);
|
|
||||||
mesaSpinner = findViewById(R.id.mesaSpinner);
|
|
||||||
|
|
||||||
Button btnVoltarAddStaff = findViewById(R.id.btnVoltarAddStaff);
|
|
||||||
if (btnVoltarAddStaff != null) {
|
|
||||||
btnVoltarAddStaff.setOnClickListener(v -> finish());
|
|
||||||
}
|
|
||||||
|
|
||||||
zones = new ArrayList<>();
|
|
||||||
zones.add("Sala");
|
|
||||||
zones.add("Esplanada");
|
|
||||||
zones.add("Balcão");
|
|
||||||
|
|
||||||
adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, zones);
|
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
zonaSpinner.setAdapter(adapter);
|
|
||||||
|
|
||||||
mesas = new ArrayList<>();
|
|
||||||
for (int i = 1; i <= 10; i++) {
|
|
||||||
mesas.add("Mesa " + i);
|
|
||||||
}
|
|
||||||
mesaAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, mesas);
|
|
||||||
mesaAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
mesaSpinner.setAdapter(mesaAdapter);
|
|
||||||
|
|
||||||
btnAddZone.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(AddStaffActivity.this);
|
|
||||||
builder.setTitle("Adicionar Zona");
|
|
||||||
|
|
||||||
final EditText input = new EditText(AddStaffActivity.this);
|
|
||||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
|
||||||
builder.setView(input);
|
|
||||||
|
|
||||||
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
String newZone = input.getText().toString();
|
|
||||||
if (!newZone.isEmpty()) {
|
|
||||||
zones.add(newZone);
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
zonaSpinner.setSelection(zones.size() - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
dialog.cancel();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
addButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
String name = nameEditText.getText().toString();
|
|
||||||
String zona = "";
|
|
||||||
if (zonaSpinner.getSelectedItem() != null) {
|
|
||||||
zona = zonaSpinner.getSelectedItem().toString();
|
|
||||||
}
|
|
||||||
String mesa = "";
|
|
||||||
if (mesaSpinner.getSelectedItem() != null) {
|
|
||||||
mesa = mesaSpinner.getSelectedItem().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!name.isEmpty()) {
|
|
||||||
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference("Staff");
|
|
||||||
String uuid = java.util.UUID.randomUUID().toString();
|
|
||||||
Staff staff = new Staff(name, zona, mesa, uuid);
|
|
||||||
databaseReference.child(uuid).setValue(staff);
|
|
||||||
|
|
||||||
Toast.makeText(AddStaffActivity.this, "Staff adicionado com sucesso!", Toast.LENGTH_SHORT).show();
|
|
||||||
finish();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(AddStaffActivity.this, "Erro: Preencha todos os campos", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,6 +9,7 @@ import android.location.LocationListener;
|
|||||||
import android.location.LocationManager;
|
import android.location.LocationManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@@ -21,13 +22,9 @@ import androidx.core.graphics.Insets;
|
|||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.core.view.WindowInsetsCompat;
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
import com.google.firebase.database.DataSnapshot;
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
import com.google.firebase.database.DatabaseError;
|
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||||
import com.google.firebase.database.DatabaseReference;
|
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
|
||||||
import com.google.firebase.database.ValueEventListener;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CheckInAntecipadoActivity extends AppCompatActivity implements LocationListener {
|
public class CheckInAntecipadoActivity extends AppCompatActivity implements LocationListener {
|
||||||
@@ -35,10 +32,10 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
|||||||
private TextView txtDistancia, txtStatus, txtEndereco;
|
private TextView txtDistancia, txtStatus, txtEndereco;
|
||||||
private Button btnConfirmarChegada, btnAbrirMapa;
|
private Button btnConfirmarChegada, btnAbrirMapa;
|
||||||
private LocationManager locationManager;
|
private LocationManager locationManager;
|
||||||
private DatabaseReference databaseReference;
|
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
|
|
||||||
private double restaurantLat, restaurantLon;
|
private double restaurantLat, restaurantLon;
|
||||||
private int securityDistance = 500; // default 500m
|
private int securityDistance = 500;
|
||||||
private boolean settingsLoaded = false;
|
private boolean settingsLoaded = false;
|
||||||
private String restaurantAddress;
|
private String restaurantAddress;
|
||||||
|
|
||||||
@@ -47,27 +44,27 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
EdgeToEdge.enable(this);
|
||||||
setContentView(R.layout.activity_checkin_antecipado);
|
setContentView(R.layout.activity_checkin_antecipado);
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.checkinRoot), (v, insets) -> {
|
|
||||||
|
View root = findViewById(R.id.checkinRoot);
|
||||||
|
if (root != null) {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
return insets;
|
return insets;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
txtDistancia = findViewById(R.id.txtDistancia);
|
txtDistancia = findViewById(R.id.txtDistancia);
|
||||||
txtStatus = findViewById(R.id.txtStatusDistancia);
|
txtStatus = findViewById(R.id.txtStatusDistancia);
|
||||||
txtEndereco = findViewById(R.id.txtEnderecoRestaurante);
|
txtEndereco = findViewById(R.id.txtEnderecoRestaurante);
|
||||||
btnConfirmarChegada = findViewById(R.id.btnConfirmarChegada);
|
btnConfirmarChegada = findViewById(R.id.btnConfirmarChegada);
|
||||||
btnAbrirMapa = findViewById(R.id.btnAbrirMapa);
|
btnAbrirMapa = findViewById(R.id.btnAbrirMapa);
|
||||||
Button back = findViewById(R.id.btnVoltar);
|
|
||||||
|
|
||||||
if (back != null) {
|
findViewById(R.id.btnVoltar).setOnClickListener(v -> finish());
|
||||||
back.setOnClickListener(v -> finish());
|
|
||||||
}
|
|
||||||
|
|
||||||
String restaurantEmail = getIntent().getStringExtra("restaurant_email");
|
String restaurantEmail = getIntent().getStringExtra("restaurant_email");
|
||||||
if (restaurantEmail != null) {
|
if (restaurantEmail != null) {
|
||||||
String restaurantId = restaurantEmail.replace(".", "_").replace("@", "_at_");
|
loadRestaurantSettings(restaurantEmail);
|
||||||
loadRestaurantSettings(restaurantId);
|
|
||||||
} else {
|
} else {
|
||||||
txtStatus.setText("Erro: Restaurante não identificado.");
|
txtStatus.setText("Erro: Restaurante não identificado.");
|
||||||
}
|
}
|
||||||
@@ -76,11 +73,11 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
|||||||
if (hasRequiredPermissions()) {
|
if (hasRequiredPermissions()) {
|
||||||
startLocationUpdates();
|
startLocationUpdates();
|
||||||
} else {
|
} else {
|
||||||
txtStatus.setText("Permissões em falta para localização/proximidade.");
|
txtStatus.setText("Permissões de localização necessárias.");
|
||||||
}
|
}
|
||||||
|
|
||||||
btnConfirmarChegada.setOnClickListener(v -> {
|
btnConfirmarChegada.setOnClickListener(v -> {
|
||||||
Toast.makeText(this, "Chegada confirmada com sucesso!", Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "Check-in realizado com sucesso!", Toast.LENGTH_LONG).show();
|
||||||
finish();
|
finish();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -98,34 +95,28 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasRequiredPermissions() {
|
private boolean hasRequiredPermissions() {
|
||||||
boolean fineLocation = ActivityCompat.checkSelfPermission(this,
|
boolean fineLocation = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
boolean btScan = ActivityCompat.checkSelfPermission(this,
|
boolean btScan = ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED;
|
||||||
Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED;
|
|
||||||
return fineLocation && btScan;
|
return fineLocation && btScan;
|
||||||
}
|
}
|
||||||
return fineLocation;
|
return fineLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadRestaurantSettings(String restaurantId) {
|
private void loadRestaurantSettings(String email) {
|
||||||
databaseReference = FirebaseDatabase.getInstance().getReference().child("Restaurantes").child(restaurantId);
|
db.collection("Restaurantes").whereEqualTo("email", email).get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||||
databaseReference.addListenerForSingleValueEvent(new ValueEventListener() {
|
if (!queryDocumentSnapshots.isEmpty()) {
|
||||||
@Override
|
QueryDocumentSnapshot doc = (QueryDocumentSnapshot) queryDocumentSnapshots.getDocuments().get(0);
|
||||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
restaurantAddress = doc.getString("address");
|
||||||
if (snapshot.exists()) {
|
Long dist = doc.getLong("securityDistance");
|
||||||
restaurantAddress = snapshot.child("address").getValue(String.class);
|
securityDistance = dist != null ? dist.intValue() : 500;
|
||||||
Integer dist = snapshot.child("securityDistance").getValue(Integer.class);
|
|
||||||
securityDistance = dist != null ? dist : 500;
|
|
||||||
|
|
||||||
if (restaurantAddress != null) {
|
if (restaurantAddress != null) {
|
||||||
txtEndereco.setText("Morada: " + restaurantAddress);
|
txtEndereco.setText("Morada: " + restaurantAddress);
|
||||||
geocodeAddress(restaurantAddress);
|
geocodeAddress(restaurantAddress);
|
||||||
} else {
|
} else {
|
||||||
txtEndereco.setText("Morada não disponível.");
|
Double lat = doc.getDouble("latitude");
|
||||||
// Fallback to old lat/lon if they exist
|
Double lon = doc.getDouble("longitude");
|
||||||
Double lat = snapshot.child("latitude").getValue(Double.class);
|
|
||||||
Double lon = snapshot.child("longitude").getValue(Double.class);
|
|
||||||
if (lat != null && lon != null) {
|
if (lat != null && lon != null) {
|
||||||
restaurantLat = lat;
|
restaurantLat = lat;
|
||||||
restaurantLon = lon;
|
restaurantLon = lon;
|
||||||
@@ -133,13 +124,7 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
txtStatus.setText("Aviso: Definições do restaurante não encontradas.");
|
txtStatus.setText("Definições do restaurante não encontradas.");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@NonNull DatabaseError error) {
|
|
||||||
txtStatus.setText("Erro ao carregar definições do restaurante.");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -153,47 +138,37 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
|||||||
restaurantLat = addresses.get(0).getLatitude();
|
restaurantLat = addresses.get(0).getLatitude();
|
||||||
restaurantLon = addresses.get(0).getLongitude();
|
restaurantLon = addresses.get(0).getLongitude();
|
||||||
settingsLoaded = true;
|
settingsLoaded = true;
|
||||||
} else {
|
|
||||||
runOnUiThread(() -> txtStatus.setText("Erro: Não foi possível localizar a morada no mapa."));
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
runOnUiThread(() -> txtStatus.setText("Erro ao obter coordenadas da morada."));
|
|
||||||
}
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLocationUpdates() {
|
private void startLocationUpdates() {
|
||||||
if (ActivityCompat.checkSelfPermission(this,
|
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) return;
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 5, this);
|
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 5, this);
|
||||||
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 5, this);
|
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000, 5, this);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
txtStatus.setText("Erro ao iniciar localização: " + e.getMessage());
|
txtStatus.setText("Erro ao iniciar localização.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLocationChanged(@NonNull Location location) {
|
public void onLocationChanged(@NonNull Location location) {
|
||||||
if (!settingsLoaded)
|
if (!settingsLoaded) return;
|
||||||
return;
|
|
||||||
|
|
||||||
float[] results = new float[1];
|
float[] results = new float[1];
|
||||||
Location.distanceBetween(location.getLatitude(), location.getLongitude(), restaurantLat, restaurantLon,
|
Location.distanceBetween(location.getLatitude(), location.getLongitude(), restaurantLat, restaurantLon, results);
|
||||||
results);
|
|
||||||
float distanceInMeters = results[0];
|
float distanceInMeters = results[0];
|
||||||
|
|
||||||
txtDistancia.setText(String.format("Distância: %.0f metros", distanceInMeters));
|
txtDistancia.setText(String.format("Distância: %.0f metros", distanceInMeters));
|
||||||
|
|
||||||
if (distanceInMeters <= securityDistance) {
|
if (distanceInMeters <= securityDistance) {
|
||||||
txtStatus.setText("Está no raio de segurança. Pode confirmar a sua chegada.");
|
txtStatus.setText("Está no raio de segurança.");
|
||||||
txtStatus.setTextColor(getResources().getColor(android.R.color.holo_green_dark));
|
txtStatus.setTextColor(getResources().getColor(android.R.color.holo_green_dark));
|
||||||
btnConfirmarChegada.setEnabled(true);
|
btnConfirmarChegada.setEnabled(true);
|
||||||
} else {
|
} else {
|
||||||
txtStatus.setText(String.format("Está fora do raio de segurança (%dm). Aproxime-se para fazer check-in.",
|
txtStatus.setText(String.format("Fora do raio de segurança (%dm).", securityDistance));
|
||||||
securityDistance));
|
|
||||||
txtStatus.setTextColor(getResources().getColor(android.R.color.holo_red_dark));
|
txtStatus.setTextColor(getResources().getColor(android.R.color.holo_red_dark));
|
||||||
btnConfirmarChegada.setEnabled(false);
|
btnConfirmarChegada.setEnabled(false);
|
||||||
}
|
}
|
||||||
@@ -202,20 +177,10 @@ public class CheckInAntecipadoActivity extends AppCompatActivity implements Loca
|
|||||||
@Override
|
@Override
|
||||||
protected void onStop() {
|
protected void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
if (locationManager != null) {
|
if (locationManager != null) locationManager.removeUpdates(this);
|
||||||
locationManager.removeUpdates(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override public void onStatusChanged(String provider, int status, Bundle extras) {}
|
||||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
@Override public void onProviderEnabled(@NonNull String provider) {}
|
||||||
}
|
@Override public void onProviderDisabled(@NonNull String provider) {}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProviderEnabled(@NonNull String provider) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onProviderDisabled(@NonNull String provider) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,313 +1,46 @@
|
|||||||
package com.example.pap_teste;
|
package com.example.pap_teste;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.core.view.ViewCompat;
|
|
||||||
import androidx.core.view.WindowInsetsCompat;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import com.example.pap_teste.models.Restaurant;
|
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||||
import com.google.android.material.chip.Chip;
|
|
||||||
import com.google.android.material.chip.ChipGroup;
|
|
||||||
import com.google.firebase.database.DataSnapshot;
|
|
||||||
import com.google.firebase.database.DatabaseError;
|
|
||||||
import com.google.firebase.database.DatabaseReference;
|
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
|
||||||
import com.google.firebase.database.ValueEventListener;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ClientDashboardActivity extends AppCompatActivity {
|
public class ClientDashboardActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private String email, displayName;
|
|
||||||
private TextView txtGreeting;
|
|
||||||
private ImageView imgProfile;
|
|
||||||
private EditText etSearch;
|
|
||||||
private ChipGroup chipGroupCategories;
|
|
||||||
private RecyclerView rvFeatured, rvMainRestaurants;
|
|
||||||
private ProgressBar progressBar;
|
|
||||||
private View layoutFeatured, layoutAllRestaurants;
|
|
||||||
|
|
||||||
private List<Restaurant> allRestaurants = new ArrayList<>();
|
|
||||||
private List<Restaurant> filteredRestaurants = new ArrayList<>();
|
|
||||||
|
|
||||||
private RestaurantAdapter mainAdapter;
|
|
||||||
private FeaturedRestaurantAdapter featuredAdapter;
|
|
||||||
|
|
||||||
private final String[] CATEGORIES = { "Tudo", "Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas" };
|
|
||||||
private String currentCategoryFilter = "Tudo";
|
|
||||||
private String currentSearchFilter = "";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
setContentView(R.layout.activity_client_dashboard_v2);
|
||||||
setContentView(R.layout.activity_client_dashboard);
|
|
||||||
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.clientRoot), (v, insets) -> {
|
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
|
||||||
return insets;
|
|
||||||
});
|
|
||||||
|
|
||||||
email = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
|
||||||
displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
|
|
||||||
|
|
||||||
initViews();
|
|
||||||
setupBottomNavigation();
|
|
||||||
setupSearch();
|
|
||||||
setupCategories();
|
|
||||||
|
|
||||||
updateGreeting();
|
|
||||||
fetchProfilePicture();
|
|
||||||
fetchRestaurants();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initViews() {
|
|
||||||
txtGreeting = findViewById(R.id.txtClientGreeting);
|
|
||||||
imgProfile = findViewById(R.id.imgProfile);
|
|
||||||
etSearch = findViewById(R.id.etSearch);
|
|
||||||
chipGroupCategories = findViewById(R.id.chipGroupCategories);
|
|
||||||
rvFeatured = findViewById(R.id.rvFeatured);
|
|
||||||
rvMainRestaurants = findViewById(R.id.rvMainRestaurants);
|
|
||||||
progressBar = findViewById(R.id.progressBar);
|
|
||||||
layoutFeatured = findViewById(R.id.layoutFeatured);
|
|
||||||
layoutAllRestaurants = findViewById(R.id.layoutAllRestaurants);
|
|
||||||
|
|
||||||
rvFeatured.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
|
||||||
rvMainRestaurants.setLayoutManager(new LinearLayoutManager(this));
|
|
||||||
|
|
||||||
// Click listener for restaurants to open booking flow
|
|
||||||
RestaurantAdapter.OnRestaurantClickListener clickListener = restaurant -> {
|
|
||||||
Intent intent = new Intent(this, ExplorarRestaurantesActivity.class);
|
|
||||||
// Reusing existing activity for details if needed, or pass data to
|
|
||||||
// NovaReservaActivity
|
|
||||||
// We pass the filter so it can maybe open directly or we just pass restaurant
|
|
||||||
// email
|
|
||||||
intent.putExtra("restaurant", restaurant);
|
|
||||||
startActivity(intent);
|
|
||||||
};
|
|
||||||
|
|
||||||
featuredAdapter = new FeaturedRestaurantAdapter(new ArrayList<>(), clickListener);
|
|
||||||
mainAdapter = new RestaurantAdapter(filteredRestaurants, clickListener);
|
|
||||||
|
|
||||||
rvFeatured.setAdapter(featuredAdapter);
|
|
||||||
rvMainRestaurants.setAdapter(mainAdapter);
|
|
||||||
|
|
||||||
// Click listener for profile picture in the header
|
|
||||||
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);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupBottomNavigation() {
|
|
||||||
BottomNavigationView bottomNav = findViewById(R.id.bottomNavigation);
|
BottomNavigationView bottomNav = findViewById(R.id.bottomNavigation);
|
||||||
|
|
||||||
|
// Initial fragment
|
||||||
|
loadFragment(new HomeFragment());
|
||||||
|
|
||||||
bottomNav.setOnItemSelectedListener(item -> {
|
bottomNav.setOnItemSelectedListener(item -> {
|
||||||
|
Fragment fragment = null;
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
|
||||||
if (id == R.id.nav_home) {
|
if (id == R.id.nav_home) {
|
||||||
return true;
|
fragment = new HomeFragment();
|
||||||
} else if (id == R.id.nav_reservations) {
|
} else if (id == R.id.nav_reservations) {
|
||||||
Intent intent = new Intent(this, MinhasReservasActivity.class);
|
fragment = new MyReservationsFragment();
|
||||||
intent.putExtra(MainActivity.EXTRA_EMAIL, email);
|
|
||||||
startActivity(intent);
|
|
||||||
return false; // Don't highlight if we open new activity
|
|
||||||
} else if (id == R.id.nav_profile) {
|
} else if (id == R.id.nav_profile) {
|
||||||
Intent intent = new Intent(this, ProfileDashboardActivity.class);
|
fragment = new ProfileFragment();
|
||||||
intent.putExtra(MainActivity.EXTRA_EMAIL, email);
|
}
|
||||||
intent.putExtra(MainActivity.EXTRA_DISPLAY_NAME, displayName);
|
|
||||||
startActivity(intent);
|
if (fragment != null) {
|
||||||
return false;
|
loadFragment(fragment);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateGreeting() {
|
private void loadFragment(Fragment fragment) {
|
||||||
if (displayName != null && !displayName.isEmpty()) {
|
getSupportFragmentManager()
|
||||||
txtGreeting.setText("Olá, " + displayName.split(" ")[0] + "!");
|
.beginTransaction()
|
||||||
} else {
|
.replace(R.id.fragmentContainer, fragment)
|
||||||
txtGreeting.setText("Olá, Visitante!");
|
.commit();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fetchProfilePicture() {
|
|
||||||
if (email == null)
|
|
||||||
return;
|
|
||||||
String documentId = email.replace(".", "_").replace("@", "_at_");
|
|
||||||
FirebaseDatabase.getInstance().getReference().child("Clientes").child(documentId).child("photoUrl")
|
|
||||||
.get().addOnSuccessListener(snapshot -> {
|
|
||||||
if (!isDestroyed() && snapshot.exists() && snapshot.getValue(String.class) != null) {
|
|
||||||
com.bumptech.glide.Glide.with(this).load(snapshot.getValue(String.class)).circleCrop()
|
|
||||||
.into(imgProfile);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fetchRestaurants() {
|
|
||||||
progressBar.setVisibility(View.VISIBLE);
|
|
||||||
layoutFeatured.setVisibility(View.GONE);
|
|
||||||
layoutAllRestaurants.setVisibility(View.GONE);
|
|
||||||
|
|
||||||
DatabaseReference usersRef = FirebaseDatabase.getInstance().getReference("Restaurantes");
|
|
||||||
usersRef.addValueEventListener(new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) {
|
|
||||||
progressBar.setVisibility(View.GONE);
|
|
||||||
allRestaurants.clear();
|
|
||||||
|
|
||||||
for (DataSnapshot ds : snapshot.getChildren()) {
|
|
||||||
String role = ds.child("role").getValue(String.class);
|
|
||||||
String accountType = ds.child("accountType").getValue(String.class);
|
|
||||||
|
|
||||||
// Aceitar todos os registos na coleção Restaurantes
|
|
||||||
if (true) {
|
|
||||||
String name = ds.child("establishmentName").getValue(String.class);
|
|
||||||
if (name == null)
|
|
||||||
name = ds.child("displayName").getValue(String.class);
|
|
||||||
String email = ds.child("email").getValue(String.class);
|
|
||||||
String cat = ds.child("category").getValue(String.class);
|
|
||||||
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
|
||||||
Double ratingAvg = ds.child("ratingAvg").getValue(Double.class);
|
|
||||||
Integer ratingCount = ds.child("ratingCount").getValue(Integer.class);
|
|
||||||
|
|
||||||
if (name != null && email != null) {
|
|
||||||
Restaurant r = new Restaurant(name, cat != null ? cat : "Vário", email, false, logoUrl);
|
|
||||||
if (ratingAvg != null)
|
|
||||||
r.setRatingAvg(ratingAvg);
|
|
||||||
if (ratingCount != null)
|
|
||||||
r.setRatingCount(ratingCount);
|
|
||||||
allRestaurants.add(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
applyFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@androidx.annotation.NonNull DatabaseError error) {
|
|
||||||
progressBar.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupCategories() {
|
|
||||||
for (String category : CATEGORIES) {
|
|
||||||
Chip chip = new Chip(this);
|
|
||||||
chip.setText(category);
|
|
||||||
chip.setCheckable(true);
|
|
||||||
chip.setClickable(true);
|
|
||||||
// Default styling
|
|
||||||
chip.setChipBackgroundColorResource(R.color.colorSurface);
|
|
||||||
chip.setTextColor(getResources().getColor(R.color.colorTextSecondary));
|
|
||||||
chip.setChipStrokeWidth(0f);
|
|
||||||
|
|
||||||
if (category.equals("Tudo")) {
|
|
||||||
chip.setChecked(true);
|
|
||||||
chip.setChipBackgroundColorResource(R.color.colorPrimary);
|
|
||||||
chip.setTextColor(getResources().getColor(R.color.white));
|
|
||||||
}
|
|
||||||
|
|
||||||
chip.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
|
||||||
if (isChecked) {
|
|
||||||
// Reset all other chips visual state
|
|
||||||
for (int i = 0; i < chipGroupCategories.getChildCount(); i++) {
|
|
||||||
Chip c = (Chip) chipGroupCategories.getChildAt(i);
|
|
||||||
if (c != chip) {
|
|
||||||
c.setChipBackgroundColorResource(R.color.colorSurface);
|
|
||||||
c.setTextColor(getResources().getColor(R.color.colorTextSecondary));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chip.setChipBackgroundColorResource(R.color.colorPrimary);
|
|
||||||
chip.setTextColor(getResources().getColor(R.color.white));
|
|
||||||
|
|
||||||
currentCategoryFilter = category;
|
|
||||||
applyFilters();
|
|
||||||
} else if (currentCategoryFilter.equals(category)) {
|
|
||||||
// Prevent unchecking the currently selected chip
|
|
||||||
chip.setChecked(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
chipGroupCategories.addView(chip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupSearch() {
|
|
||||||
etSearch.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
currentSearchFilter = s.toString().toLowerCase().trim();
|
|
||||||
applyFilters();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void applyFilters() {
|
|
||||||
filteredRestaurants.clear();
|
|
||||||
String normalizedSearch = normalizeString(currentSearchFilter);
|
|
||||||
|
|
||||||
for (Restaurant r : allRestaurants) {
|
|
||||||
boolean matchesCategory = currentCategoryFilter.equals("Tudo")
|
|
||||||
|| currentCategoryFilter.equals(r.getCategory());
|
|
||||||
|
|
||||||
String normalizedName = normalizeString(r.getName());
|
|
||||||
boolean matchesSearch = currentSearchFilter.isEmpty() || normalizedName.contains(normalizedSearch);
|
|
||||||
|
|
||||||
if (matchesCategory && matchesSearch) {
|
|
||||||
filteredRestaurants.add(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mainAdapter.notifyDataSetChanged();
|
|
||||||
|
|
||||||
// Update featured (just the first 3 for demo, or based on a specific logic)
|
|
||||||
List<Restaurant> featuredList = new ArrayList<>();
|
|
||||||
for (int i = 0; i < Math.min(3, filteredRestaurants.size()); i++) {
|
|
||||||
featuredList.add(filteredRestaurants.get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
featuredAdapter = new FeaturedRestaurantAdapter(featuredList,
|
|
||||||
mainAdapter instanceof RestaurantAdapter ? restaurant -> {
|
|
||||||
Intent intent = new Intent(this, ExplorarRestaurantesActivity.class);
|
|
||||||
intent.putExtra("restaurant", restaurant);
|
|
||||||
startActivity(intent);
|
|
||||||
} : null);
|
|
||||||
rvFeatured.setAdapter(featuredAdapter);
|
|
||||||
|
|
||||||
layoutFeatured.setVisibility(featuredList.isEmpty() ? View.GONE : View.VISIBLE);
|
|
||||||
layoutAllRestaurants.setVisibility(filteredRestaurants.isEmpty() ? View.GONE : View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String normalizeString(String str) {
|
|
||||||
if (str == null)
|
|
||||||
return "";
|
|
||||||
// Remove accents and special marks, and convert to lowercase
|
|
||||||
String normalized = java.text.Normalizer.normalize(str, java.text.Normalizer.Form.NFD);
|
|
||||||
normalized = normalized.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
|
|
||||||
return normalized.toLowerCase().trim();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
75
app/src/main/java/com/example/pap_teste/DataSeeder.java
Normal file
75
app/src/main/java/com/example/pap_teste/DataSeeder.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Restaurant;
|
||||||
|
import com.google.firebase.firestore.CollectionReference;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class DataSeeder {
|
||||||
|
|
||||||
|
public static void seedRestaurants() {
|
||||||
|
com.google.firebase.firestore.CollectionReference restaurants = FirestoreManager.getInstance().getRestaurantsCollection();
|
||||||
|
|
||||||
|
List<Restaurant> sampleRestaurants = new ArrayList<>();
|
||||||
|
|
||||||
|
sampleRestaurants.add(createRestaurant("The Italian Job", "Italian",
|
||||||
|
"Authentic wood-fired pizzas and homemade pasta in a cozy setting.",
|
||||||
|
"https://images.unsplash.com/photo-1555396273-367ea4eb4db5", 4.8, "Rua de Itália, 123, Lisboa"));
|
||||||
|
|
||||||
|
sampleRestaurants.add(createRestaurant("Sushi Zen", "Japanese",
|
||||||
|
"Premium sushi experience with fresh ingredients flown in daily.",
|
||||||
|
"https://images.unsplash.com/photo-1579871494447-9811cf80d66c", 4.9, "Avenida do Japão, 45, Porto"));
|
||||||
|
|
||||||
|
sampleRestaurants.add(createRestaurant("Burger Haven", "Burgers",
|
||||||
|
"Gourmet burgers made with 100% aged beef and unique toppings.",
|
||||||
|
"https://images.unsplash.com/photo-1571091718767-18b5b1457add", 4.5, "Praça do Hambúrguer, 10, Braga"));
|
||||||
|
|
||||||
|
sampleRestaurants.add(createRestaurant("Taco Libre", "Mexican",
|
||||||
|
"Vibrant street-style Mexican food and craft margaritas.",
|
||||||
|
"https://images.unsplash.com/photo-1565299585323-38d6b0865b47", 4.6, "Rua do México, 88, Faro"));
|
||||||
|
|
||||||
|
sampleRestaurants.add(createRestaurant("Steakhouse Prime", "Steakhouse",
|
||||||
|
"Expertly grilled premium cuts and an extensive wine list.",
|
||||||
|
"https://images.unsplash.com/photo-1546241072-48010ad28c2c", 4.7, "Travessa do Bife, 5, Coimbra"));
|
||||||
|
|
||||||
|
sampleRestaurants.add(createRestaurant("Le Petit Bistro", "French",
|
||||||
|
"A slice of Paris serving classic French dishes and fine wine.",
|
||||||
|
"https://images.unsplash.com/photo-1550966842-28a2a2d3efd1", 4.4, "Rua de França, 22, Évora"));
|
||||||
|
|
||||||
|
sampleRestaurants.add(createRestaurant("Green Garden", "Vegetarian",
|
||||||
|
"Sustainable and delicious plant-based cuisine for everyone.",
|
||||||
|
"https://images.unsplash.com/photo-1512621776951-a57141f2eefd", 4.6, "Alameda Verde, 15, Aveiro"));
|
||||||
|
|
||||||
|
sampleRestaurants.add(createRestaurant("Ocean's Catch", "Seafood",
|
||||||
|
"The freshest seafood and shellfish with a beautiful ocean view.",
|
||||||
|
"https://images.unsplash.com/photo-1551248429-42405d2d9cd8", 4.8, "Estrada da Praia, 1, Cascais"));
|
||||||
|
|
||||||
|
for (Restaurant r : sampleRestaurants) {
|
||||||
|
restaurants.document().set(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Restaurant createRestaurant(String name, String cuisine, String desc, String url, double rating, String address) {
|
||||||
|
Restaurant r = new Restaurant();
|
||||||
|
r.setName(name);
|
||||||
|
r.setCuisine(cuisine);
|
||||||
|
r.setDescription(desc);
|
||||||
|
r.setImageURL(url);
|
||||||
|
r.setRating(rating);
|
||||||
|
r.setAddress(address);
|
||||||
|
|
||||||
|
Map<String, String> hours = new HashMap<>();
|
||||||
|
hours.put("open", "12:00");
|
||||||
|
hours.put("close", "23:00");
|
||||||
|
r.setOpeningHours(hours);
|
||||||
|
|
||||||
|
List<String> slots = new ArrayList<>();
|
||||||
|
slots.add("12:00"); slots.add("13:00"); slots.add("14:00");
|
||||||
|
slots.add("19:00"); slots.add("20:00"); slots.add("21:00"); slots.add("22:00");
|
||||||
|
r.setAvailableSlots(slots);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import com.bumptech.glide.Glide;
|
|
||||||
import com.google.firebase.storage.FirebaseStorage;
|
|
||||||
import com.google.firebase.storage.StorageReference;
|
|
||||||
import java.util.UUID;
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
|
||||||
|
|
||||||
public class DefinicoesAdminActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
private EditText inputRadius, inputAddress;
|
|
||||||
private android.widget.Spinner spinnerCategory;
|
|
||||||
private ImageView imgLogo;
|
|
||||||
private DatabaseReference databaseReference;
|
|
||||||
private String documentId;
|
|
||||||
private String photoUrl;
|
|
||||||
private ActivityResultLauncher<Intent> imagePickerLauncher;
|
|
||||||
private String[] categories = {"Carnes", "Massas", "Sushi", "Pizzas", "Sobremesas", "Italiana", "Moderna"};
|
|
||||||
|
|
||||||
@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("Restaurantes");
|
|
||||||
|
|
||||||
inputRadius = findViewById(R.id.inputRadius);
|
|
||||||
inputAddress = findViewById(R.id.inputAddress);
|
|
||||||
spinnerCategory = findViewById(R.id.spinnerCategory);
|
|
||||||
imgLogo = findViewById(R.id.imgRestaurantLogo);
|
|
||||||
|
|
||||||
android.widget.ArrayAdapter<String> adapter = new android.widget.ArrayAdapter<>(this,
|
|
||||||
android.R.layout.simple_spinner_item, categories);
|
|
||||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
spinnerCategory.setAdapter(adapter);
|
|
||||||
|
|
||||||
Button btnSave = findViewById(R.id.btnSaveSettings);
|
|
||||||
Button btnBack = findViewById(R.id.btnVoltar);
|
|
||||||
Button btnChangeLogo = findViewById(R.id.btnChangeLogo);
|
|
||||||
|
|
||||||
if (btnBack != null) {
|
|
||||||
btnBack.setOnClickListener(v -> finish());
|
|
||||||
}
|
|
||||||
|
|
||||||
imagePickerLauncher = registerForActivityResult(
|
|
||||||
new ActivityResultContracts.StartActivityForResult(),
|
|
||||||
result -> {
|
|
||||||
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
|
|
||||||
Uri imageUri = result.getData().getData();
|
|
||||||
if (imageUri != null) {
|
|
||||||
uploadImageToFirebase(imageUri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
btnChangeLogo.setOnClickListener(v -> {
|
|
||||||
String[] options = {"Galeria", "URL da Imagem"};
|
|
||||||
new androidx.appcompat.app.AlertDialog.Builder(this)
|
|
||||||
.setTitle("Escolher Logótipo")
|
|
||||||
.setItems(options, (dialog, which) -> {
|
|
||||||
if (which == 0) {
|
|
||||||
Intent intent = new Intent(Intent.ACTION_PICK);
|
|
||||||
intent.setType("image/*");
|
|
||||||
imagePickerLauncher.launch(intent);
|
|
||||||
} else {
|
|
||||||
showUrlInputDialog();
|
|
||||||
}
|
|
||||||
}).show();
|
|
||||||
});
|
|
||||||
|
|
||||||
loadCurrentSettings();
|
|
||||||
|
|
||||||
btnSave.setOnClickListener(v -> saveSettings());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showUrlInputDialog() {
|
|
||||||
androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(this);
|
|
||||||
builder.setTitle("Inserir URL da Imagem");
|
|
||||||
|
|
||||||
final EditText input = new EditText(this);
|
|
||||||
input.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_URI);
|
|
||||||
builder.setView(input);
|
|
||||||
|
|
||||||
builder.setPositiveButton("Confirmar", (dialog, which) -> {
|
|
||||||
String url = input.getText().toString().trim();
|
|
||||||
if (!TextUtils.isEmpty(url)) {
|
|
||||||
this.photoUrl = url;
|
|
||||||
Glide.with(this).load(photoUrl).circleCrop().into(imgLogo);
|
|
||||||
Toast.makeText(this, "Logótipo definido!", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.setNegativeButton("Cancelar", (dialog, which) -> dialog.cancel());
|
|
||||||
builder.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void uploadImageToFirebase(Uri imageUri) {
|
|
||||||
if (documentId == null) return;
|
|
||||||
|
|
||||||
StorageReference storageRef = FirebaseStorage.getInstance().getReference()
|
|
||||||
.child("restaurant_logos/" + UUID.randomUUID().toString());
|
|
||||||
|
|
||||||
storageRef.putFile(imageUri).addOnSuccessListener(taskSnapshot -> {
|
|
||||||
storageRef.getDownloadUrl().addOnSuccessListener(uri -> {
|
|
||||||
photoUrl = uri.toString();
|
|
||||||
Glide.with(this).load(photoUrl).circleCrop().into(imgLogo);
|
|
||||||
});
|
|
||||||
}).addOnFailureListener(e -> {
|
|
||||||
Toast.makeText(this, "Falha no upload: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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("address")) {
|
|
||||||
String addr = snapshot.child("address").getValue(String.class);
|
|
||||||
inputAddress.setText(addr != null ? addr : "");
|
|
||||||
}
|
|
||||||
if (snapshot.hasChild("category")) {
|
|
||||||
String cat = snapshot.child("category").getValue(String.class);
|
|
||||||
if (cat != null) {
|
|
||||||
for (int i = 0; i < categories.length; i++) {
|
|
||||||
if (categories[i].equalsIgnoreCase(cat)) {
|
|
||||||
spinnerCategory.setSelection(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (snapshot.hasChild("logoUrl")) {
|
|
||||||
photoUrl = snapshot.child("logoUrl").getValue(String.class);
|
|
||||||
if (photoUrl != null && !photoUrl.isEmpty()) {
|
|
||||||
Glide.with(DefinicoesAdminActivity.this).load(photoUrl).circleCrop().into(imgLogo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 addressStr = inputAddress.getText().toString().trim();
|
|
||||||
String selectedCategory = spinnerCategory.getSelectedItem().toString();
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(radiusStr) || TextUtils.isEmpty(addressStr)) {
|
|
||||||
Toast.makeText(this, "Preencha todos os campos.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
int radius = Integer.parseInt(radiusStr);
|
|
||||||
|
|
||||||
Map<String, Object> updates = new HashMap<>();
|
|
||||||
updates.put("securityDistance", radius);
|
|
||||||
updates.put("address", addressStr);
|
|
||||||
updates.put("category", selectedCategory);
|
|
||||||
if (photoUrl != null) {
|
|
||||||
updates.put("logoUrl", photoUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
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, "Raio inválido.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
package com.example.pap_teste;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
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 java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class DetalhesReservasActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
private final List<com.example.pap_teste.models.Reserva> reservas = new ArrayList<>();
|
|
||||||
private ArrayAdapter<String> adapter;
|
|
||||||
private ListView listReservas;
|
|
||||||
private TextView txtInfo;
|
|
||||||
private TextView txtNotas;
|
|
||||||
private TextView txtEstado;
|
|
||||||
private TextView txtMensagem;
|
|
||||||
private Button btnConfirmar, btnRecusar, btnApagar;
|
|
||||||
private int selectedIndex = -1;
|
|
||||||
private String restaurantEmail;
|
|
||||||
private com.google.firebase.database.DatabaseReference databaseReference;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
EdgeToEdge.enable(this);
|
|
||||||
setContentView(R.layout.activity_detalhes_reservas);
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.detalhesReservasRoot), (v, insets) -> {
|
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
|
||||||
return insets;
|
|
||||||
});
|
|
||||||
|
|
||||||
restaurantEmail = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
|
||||||
if (restaurantEmail == null) {
|
|
||||||
if (com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null) {
|
|
||||||
restaurantEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail();
|
|
||||||
} else {
|
|
||||||
restaurantEmail = "sabor_arte@restaurante.com";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bindViews();
|
|
||||||
setupList();
|
|
||||||
setupActions();
|
|
||||||
loadReservas();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindViews() {
|
|
||||||
listReservas = findViewById(R.id.listReservas);
|
|
||||||
txtInfo = findViewById(R.id.txtReservaInfo);
|
|
||||||
txtNotas = findViewById(R.id.txtReservaNotas);
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadReservas() {
|
|
||||||
databaseReference = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas");
|
|
||||||
databaseReference.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(
|
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
reservas.clear();
|
|
||||||
String targetEmail = restaurantEmail != null ? restaurantEmail.trim() : "";
|
|
||||||
for (com.google.firebase.database.DataSnapshot data : snapshot.getChildren()) {
|
|
||||||
com.example.pap_teste.models.Reserva r = data
|
|
||||||
.getValue(com.example.pap_teste.models.Reserva.class);
|
|
||||||
if (r != null && r.getEstado() != null && !"Arquivada".equals(r.getEstado())) {
|
|
||||||
String rEmail = r.getRestauranteEmail();
|
|
||||||
if (rEmail != null && rEmail.trim().equalsIgnoreCase(targetEmail)) {
|
|
||||||
reservas.add(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
refreshList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(
|
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
Toast.makeText(DetalhesReservasActivity.this, "Erro ao carregar reservas.", Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupList() {
|
|
||||||
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1);
|
|
||||||
listReservas.setAdapter(adapter);
|
|
||||||
|
|
||||||
listReservas.setOnItemClickListener((parent, view, position, id) -> {
|
|
||||||
selectedIndex = position;
|
|
||||||
mostrarDetalhe(reservas.get(position));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupActions() {
|
|
||||||
if (btnConfirmar != null) {
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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/Arquivar Reserva")
|
|
||||||
.setMessage("Tem a certeza que deseja limpar esta reserva da lista?")
|
|
||||||
.setPositiveButton("Sim", (dialog, which) -> {
|
|
||||||
com.example.pap_teste.models.Reserva r = reservas.get(selectedIndex);
|
|
||||||
String idReserva = r.getId();
|
|
||||||
|
|
||||||
if ("Concluída".equals(r.getEstado()) || r.getEstado().startsWith("Confirmada")) {
|
|
||||||
databaseReference.child(idReserva).child("estado").setValue("Arquivada").addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
clearSelectionAndShowMessage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
databaseReference.child(idReserva).removeValue().addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
clearSelectionAndShowMessage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton("Voltar", null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearSelectionAndShowMessage() {
|
|
||||||
selectedIndex = -1;
|
|
||||||
txtInfo.setText("Selecione uma reserva");
|
|
||||||
txtNotas.setText("");
|
|
||||||
txtEstado.setText("Estado:");
|
|
||||||
txtMensagem.setText("Reserva processada e apagada da vista.");
|
|
||||||
toggleButtons(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void atualizarEstadoSelecionado(String novoEstado) {
|
|
||||||
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 -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
txtMensagem
|
|
||||||
.setText(String.format("Reserva de %s marcada como %s.", item.getClienteEmail(), novoEstado));
|
|
||||||
mostrarDetalhe(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mostrarDetalhe(com.example.pap_teste.models.Reserva item) {
|
|
||||||
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()));
|
|
||||||
toggleButtons(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleButtons(com.example.pap_teste.models.Reserva 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.getEstado()) {
|
|
||||||
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() {
|
|
||||||
adapter.clear();
|
|
||||||
for (com.example.pap_teste.models.Reserva item : reservas) {
|
|
||||||
String resumo = String.format("%s - %s (%s) • %s", item.getHora(), item.getData(), item.getEstado(),
|
|
||||||
item.getClienteEmail());
|
|
||||||
adapter.add(resumo);
|
|
||||||
}
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,250 +1,13 @@
|
|||||||
package com.example.pap_teste;
|
package com.example.pap_teste;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.graphics.Insets;
|
|
||||||
import androidx.core.view.ViewCompat;
|
|
||||||
import androidx.core.view.WindowInsetsCompat;
|
|
||||||
|
|
||||||
public class EstablishmentDashboardActivity extends AppCompatActivity {
|
public class EstablishmentDashboardActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
// Temporariamente usando o layout do cliente ou um placeholder se necessário
|
||||||
setContentView(R.layout.activity_establishment_dashboard);
|
setContentView(R.layout.activity_client_dashboard);
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.establishmentRoot), (v, insets) -> {
|
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
|
||||||
return insets;
|
|
||||||
});
|
|
||||||
|
|
||||||
TextView txtTitle = findViewById(R.id.txtEstabTitle);
|
|
||||||
TextView txtSubtitle = findViewById(R.id.txtEstabSubtitle);
|
|
||||||
TextView txtRole = findViewById(R.id.txtEstabRole);
|
|
||||||
|
|
||||||
String actionMode = getIntent().getStringExtra(MainActivity.EXTRA_ACTION_MODE);
|
|
||||||
String displayName = getIntent().getStringExtra(MainActivity.EXTRA_DISPLAY_NAME);
|
|
||||||
String role = getIntent().getStringExtra(MainActivity.EXTRA_ROLE);
|
|
||||||
|
|
||||||
boolean isNewAccount = "CRIAR".equalsIgnoreCase(actionMode);
|
|
||||||
txtTitle.setText(displayName != null ? displayName : "Estabelecimento");
|
|
||||||
txtRole.setText(String.format("Função: %s", role != null ? role : "ADMIN"));
|
|
||||||
txtSubtitle.setText(isNewAccount
|
|
||||||
? "Perfil criado. Configure horários, mesas e abra reservas."
|
|
||||||
: "Dashboard operacional. Acompanhe as reservas em tempo real.");
|
|
||||||
|
|
||||||
Button btnOpenWalkIns = findViewById(R.id.btnAbrirEspera);
|
|
||||||
Button btnStaff = findViewById(R.id.btnGestaoStaff);
|
|
||||||
Button btnGerirMesas = findViewById(R.id.btnGerirMesas);
|
|
||||||
Button btnDetails = findViewById(R.id.btnDetalhesReservas);
|
|
||||||
Button btnSettings = findViewById(R.id.btnDefinicoes);
|
|
||||||
Button btnBack = findViewById(R.id.btnVoltar);
|
|
||||||
|
|
||||||
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; // Can't proceed without email
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final String finalEmail = email;
|
|
||||||
|
|
||||||
btnOpenWalkIns.setOnClickListener(v -> {
|
|
||||||
Intent intent = new Intent(this, ListaEsperaActivity.class);
|
|
||||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
|
|
||||||
btnStaff.setOnClickListener(v -> {
|
|
||||||
Intent intent = new Intent(this, GestaoStaffActivity.class);
|
|
||||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
|
|
||||||
btnGerirMesas.setOnClickListener(v -> {
|
|
||||||
Intent intent = new Intent(this, GerirMesasActivity.class);
|
|
||||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
|
|
||||||
btnDetails.setOnClickListener(v -> {
|
|
||||||
Intent intent = new Intent(this, DetalhesReservasActivity.class);
|
|
||||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
|
|
||||||
btnSettings.setOnClickListener(v -> {
|
|
||||||
Intent intent = new Intent(this, DefinicoesAdminActivity.class);
|
|
||||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
|
|
||||||
findViewById(R.id.cardReservasHoje).setOnClickListener(v -> {
|
|
||||||
Intent intent = new Intent(this, DetalhesReservasActivity.class);
|
|
||||||
intent.putExtra(MainActivity.EXTRA_EMAIL, finalEmail);
|
|
||||||
startActivity(intent);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (btnBack != null) {
|
|
||||||
|
|
||||||
btnBack.setOnClickListener(v -> finish());
|
|
||||||
}
|
|
||||||
|
|
||||||
loadProximasReservas();
|
|
||||||
loadEstatisticas();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadProximasReservas() {
|
|
||||||
android.widget.LinearLayout llProximasReservas = findViewById(R.id.llProximasReservas);
|
|
||||||
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 targetEmail = email != null ? email.trim() : "";
|
|
||||||
|
|
||||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
|
||||||
String todayDate = cal.get(java.util.Calendar.DAY_OF_MONTH) + "/" +
|
|
||||||
(cal.get(java.util.Calendar.MONTH) + 1) + "/" +
|
|
||||||
cal.get(java.util.Calendar.YEAR);
|
|
||||||
|
|
||||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas")
|
|
||||||
.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
llProximasReservas.removeAllViews();
|
|
||||||
boolean found = false;
|
|
||||||
|
|
||||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
|
||||||
com.example.pap_teste.models.Reserva r = ds.getValue(com.example.pap_teste.models.Reserva.class);
|
|
||||||
if (r != null && r.getEstado() != null && (r.getEstado().startsWith("Confirmada") || r.getEstado().equals("Concluída")) && todayDate.equals(r.getData())) {
|
|
||||||
String rEmail = r.getRestauranteEmail();
|
|
||||||
if (rEmail != null && rEmail.trim().equalsIgnoreCase(targetEmail)) {
|
|
||||||
found = true;
|
|
||||||
addReservaView(llProximasReservas, r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
TextView empty = new TextView(EstablishmentDashboardActivity.this);
|
|
||||||
empty.setText("Não há reservas confirmadas para hoje.");
|
|
||||||
empty.setTextColor(android.graphics.Color.parseColor("#5F5F5F"));
|
|
||||||
empty.setTextSize(14f);
|
|
||||||
llProximasReservas.addView(empty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addReservaView(android.widget.LinearLayout container, com.example.pap_teste.models.Reserva r) {
|
|
||||||
TextView title = new TextView(this);
|
|
||||||
title.setText(String.format("%s - %s", r.getHora(), r.getData()));
|
|
||||||
title.setTextColor(android.graphics.Color.BLACK);
|
|
||||||
title.setTextSize(16f);
|
|
||||||
title.setTypeface(null, android.graphics.Typeface.BOLD);
|
|
||||||
|
|
||||||
TextView subtitle = new TextView(this);
|
|
||||||
subtitle.setText(String.format("Cliente: %s • %d pessoas", r.getClienteEmail(), r.getPessoas()));
|
|
||||||
subtitle.setTextColor(android.graphics.Color.parseColor("#5F5F5F"));
|
|
||||||
subtitle.setTextSize(14f);
|
|
||||||
|
|
||||||
android.widget.LinearLayout.LayoutParams params = new android.widget.LinearLayout.LayoutParams(
|
|
||||||
android.widget.LinearLayout.LayoutParams.MATCH_PARENT, android.widget.LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
subtitle.setLayoutParams(params);
|
|
||||||
|
|
||||||
android.view.View divider = new android.view.View(this);
|
|
||||||
android.widget.LinearLayout.LayoutParams divParams = new android.widget.LinearLayout.LayoutParams(
|
|
||||||
android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 2);
|
|
||||||
divParams.setMargins(0, 24, 0, 24);
|
|
||||||
divider.setLayoutParams(divParams);
|
|
||||||
divider.setBackgroundColor(android.graphics.Color.parseColor("#EEEEEE"));
|
|
||||||
|
|
||||||
container.addView(title);
|
|
||||||
container.addView(subtitle);
|
|
||||||
container.addView(divider);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadEstatisticas() {
|
|
||||||
TextView txtReservasHoje = findViewById(R.id.txtReservasHojeDash);
|
|
||||||
TextView txtMesasLivres = findViewById(R.id.txtMesasLivresDash);
|
|
||||||
TextView txtListaEspera = findViewById(R.id.txtListaEsperaDash);
|
|
||||||
|
|
||||||
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() : "";
|
|
||||||
|
|
||||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
|
||||||
String todayDate = cal.get(java.util.Calendar.DAY_OF_MONTH) + "/" +
|
|
||||||
(cal.get(java.util.Calendar.MONTH) + 1) + "/" +
|
|
||||||
cal.get(java.util.Calendar.YEAR);
|
|
||||||
|
|
||||||
com.google.firebase.database.DatabaseReference refReservas = com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reservas");
|
|
||||||
refReservas.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
int reservasHoje = 0;
|
|
||||||
int listaEspera = 0;
|
|
||||||
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 (r.getEstado() != null) {
|
|
||||||
if ((r.getEstado().startsWith("Confirmada") || r.getEstado().equals("Concluída") || r.getEstado().equals("Arquivada")) && todayDate.equals(r.getData())) {
|
|
||||||
reservasHoje++;
|
|
||||||
} else if ("Pendente".equals(r.getEstado())) {
|
|
||||||
listaEspera++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (txtReservasHoje != null) txtReservasHoje.setText(String.format(java.util.Locale.US, "%02d", reservasHoje));
|
|
||||||
if (txtListaEspera != null) txtListaEspera.setText(String.format(java.util.Locale.US, "%02d", listaEspera));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Mesas")
|
|
||||||
.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
int mesasLivres = 0;
|
|
||||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
|
||||||
com.example.pap_teste.models.Mesa mesa = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
|
||||||
if (mesa != null && mesa.getRestauranteEmail() != null && mesa.getRestauranteEmail().trim().equalsIgnoreCase(finalEmail)) {
|
|
||||||
if ("Livre".equalsIgnoreCase(mesa.getEstado())) {
|
|
||||||
mesasLivres++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (txtMesasLivres != null) txtMesasLivres.setText(String.format(java.util.Locale.US, "%02d", mesasLivres));
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,43 @@
|
|||||||
package com.example.pap_teste;
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.core.view.WindowInsetsCompat;
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import android.widget.Button;
|
import com.example.pap_teste.models.Reserva;
|
||||||
|
import com.example.pap_teste.models.Restaurant;
|
||||||
|
import com.example.pap_teste.models.Review;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
import com.google.firebase.firestore.Query;
|
||||||
|
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
||||||
|
|
||||||
@@ -17,18 +46,20 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private State currentState = State.LIST;
|
private State currentState = State.LIST;
|
||||||
private com.example.pap_teste.models.Restaurant selectedRestaurant = null;
|
private Restaurant selectedRestaurant = null;
|
||||||
|
|
||||||
private android.view.View scrollReservaDetails;
|
private View scrollReservaDetails;
|
||||||
private androidx.recyclerview.widget.RecyclerView rvRestaurants;
|
private RecyclerView rvRestaurants;
|
||||||
private android.widget.TextView txtTitle;
|
private TextView txtTitle;
|
||||||
private android.widget.ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
|
||||||
private String selectedDate = null;
|
private String selectedDate = null;
|
||||||
private String selectedTime = null;
|
private String selectedTime = null;
|
||||||
|
|
||||||
private final androidx.activity.result.ActivityResultLauncher<android.content.Intent> photoPickerLauncher = registerForActivityResult(
|
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
|
||||||
|
private final ActivityResultLauncher<Intent> photoPickerLauncher = registerForActivityResult(
|
||||||
|
new ActivityResultContracts.StartActivityForResult(),
|
||||||
result -> {
|
result -> {
|
||||||
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
|
if (result.getResultCode() == RESULT_OK && result.getData() != null) {
|
||||||
android.net.Uri imageUri = result.getData().getData();
|
android.net.Uri imageUri = result.getData().getData();
|
||||||
@@ -43,11 +74,15 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
EdgeToEdge.enable(this);
|
||||||
setContentView(R.layout.activity_explorar_restaurantes);
|
setContentView(R.layout.activity_explorar_restaurantes);
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.explorarRoot), (v, insets) -> {
|
|
||||||
|
View root = findViewById(R.id.explorarRoot);
|
||||||
|
if (root != null) {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
return insets;
|
return insets;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
rvRestaurants = findViewById(R.id.rvRestaurants);
|
rvRestaurants = findViewById(R.id.rvRestaurants);
|
||||||
scrollReservaDetails = findViewById(R.id.scrollReservaDetails);
|
scrollReservaDetails = findViewById(R.id.scrollReservaDetails);
|
||||||
@@ -59,11 +94,6 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
back.setOnClickListener(v -> handleBackNavigation());
|
back.setOnClickListener(v -> handleBackNavigation());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getIntent().hasExtra("restaurant")) {
|
|
||||||
selectedRestaurant = (com.example.pap_teste.models.Restaurant) getIntent().getSerializableExtra("restaurant");
|
|
||||||
currentState = State.DETAILS;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupRestaurantList();
|
setupRestaurantList();
|
||||||
updateViewState();
|
updateViewState();
|
||||||
}
|
}
|
||||||
@@ -78,9 +108,8 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateViewState() {
|
private void updateViewState() {
|
||||||
rvRestaurants.setVisibility(currentState == State.LIST ? android.view.View.VISIBLE : android.view.View.GONE);
|
rvRestaurants.setVisibility(currentState == State.LIST ? View.VISIBLE : View.GONE);
|
||||||
scrollReservaDetails
|
scrollReservaDetails.setVisibility(currentState == State.DETAILS ? View.VISIBLE : View.GONE);
|
||||||
.setVisibility(currentState == State.DETAILS ? android.view.View.VISIBLE : android.view.View.GONE);
|
|
||||||
|
|
||||||
if (currentState == State.LIST) {
|
if (currentState == State.LIST) {
|
||||||
String filter = getIntent().getStringExtra("category_filter");
|
String filter = getIntent().getStringExtra("category_filter");
|
||||||
@@ -93,77 +122,47 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadRestaurantRating() {
|
private void loadRestaurantRating() {
|
||||||
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null)
|
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return;
|
||||||
return;
|
|
||||||
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
|
||||||
|
|
||||||
android.widget.TextView txtAvgRating = findViewById(R.id.txtAvgRating);
|
TextView txtAvgRating = findViewById(R.id.txtAvgRating);
|
||||||
android.widget.TextView txtTotalReviews = findViewById(R.id.txtTotalReviews);
|
TextView txtTotalReviews = findViewById(R.id.txtTotalReviews);
|
||||||
|
|
||||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Restaurantes")
|
db.collection("Restaurantes").whereEqualTo("email", selectedRestaurant.getEmail()).get()
|
||||||
.child(encodedEmail)
|
.addOnSuccessListener(queryDocumentSnapshots -> {
|
||||||
.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
if (!queryDocumentSnapshots.isEmpty()) {
|
||||||
@Override
|
QueryDocumentSnapshot doc = (QueryDocumentSnapshot) queryDocumentSnapshots.getDocuments().get(0);
|
||||||
public void onDataChange(
|
Double avg = doc.getDouble("ratingAvg");
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
Long count = doc.getLong("ratingCount");
|
||||||
Double avg = snapshot.child("ratingAvg").getValue(Double.class);
|
|
||||||
Integer count = snapshot.child("ratingCount").getValue(Integer.class);
|
|
||||||
|
|
||||||
if (avg != null && count != null && count > 0) {
|
if (avg != null && count != null && count > 0) {
|
||||||
txtAvgRating.setText(String.format(java.util.Locale.getDefault(), "%.1f", avg));
|
txtAvgRating.setText(String.format(Locale.getDefault(), "%.1f", avg));
|
||||||
txtTotalReviews
|
txtTotalReviews.setText(String.format(Locale.getDefault(), "(%d avaliações)", count));
|
||||||
.setText(String.format(java.util.Locale.getDefault(), "(%d avaliações)", count));
|
|
||||||
} else {
|
} else {
|
||||||
txtAvgRating.setText("0.0");
|
txtAvgRating.setText("0.0");
|
||||||
txtTotalReviews.setText("(0 avaliações)");
|
txtTotalReviews.setText("(0 avaliações)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(
|
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupRestaurantList() {
|
private void setupRestaurantList() {
|
||||||
String filter = getIntent().getStringExtra("category_filter");
|
String filter = getIntent().getStringExtra("category_filter");
|
||||||
java.util.List<com.example.pap_teste.models.Restaurant> restaurantsList = new java.util.ArrayList<>();
|
List<Restaurant> restaurantsList = new ArrayList<>();
|
||||||
com.google.firebase.database.DatabaseReference usersRef = com.google.firebase.database.FirebaseDatabase
|
|
||||||
.getInstance().getReference("Restaurantes");
|
|
||||||
|
|
||||||
com.google.firebase.database.Query query = usersRef;
|
Query query = db.collection("Restaurantes");
|
||||||
if (filter != null) {
|
if (filter != null) {
|
||||||
query = usersRef.orderByChild("category").equalTo(filter);
|
query = query.whereEqualTo("category", filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressBar != null)
|
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||||
progressBar.setVisibility(android.view.View.VISIBLE);
|
|
||||||
|
|
||||||
query.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
query.get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||||
@Override
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
if (progressBar != null)
|
|
||||||
progressBar.setVisibility(android.view.View.GONE);
|
|
||||||
restaurantsList.clear();
|
restaurantsList.clear();
|
||||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||||
String role = ds.child("role").getValue(String.class);
|
Restaurant r = doc.toObject(Restaurant.class);
|
||||||
String accountType = ds.child("accountType").getValue(String.class);
|
r.setId(doc.getId());
|
||||||
|
restaurantsList.add(r);
|
||||||
// Aceitar todos os registos na coleção Restaurantes
|
|
||||||
if (true) {
|
|
||||||
String name = ds.child("establishmentName").getValue(String.class);
|
|
||||||
if (name == null)
|
|
||||||
name = ds.child("displayName").getValue(String.class);
|
|
||||||
String email = ds.child("email").getValue(String.class);
|
|
||||||
String cat = ds.child("category").getValue(String.class);
|
|
||||||
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
|
||||||
|
|
||||||
if (name != null && email != null) {
|
|
||||||
restaurantsList
|
|
||||||
.add(new com.example.pap_teste.models.Restaurant(name, cat, email, false, logoUrl));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RestaurantAdapter adapter = new RestaurantAdapter(restaurantsList, restaurant -> {
|
RestaurantAdapter adapter = new RestaurantAdapter(restaurantsList, restaurant -> {
|
||||||
@@ -172,94 +171,65 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
updateViewState();
|
updateViewState();
|
||||||
});
|
});
|
||||||
rvRestaurants.setAdapter(adapter);
|
rvRestaurants.setAdapter(adapter);
|
||||||
}
|
}).addOnFailureListener(e -> {
|
||||||
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
@Override
|
Toast.makeText(this, "Erro ao carregar restaurantes.", Toast.LENGTH_SHORT).show();
|
||||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
if (progressBar != null)
|
|
||||||
progressBar.setVisibility(android.view.View.GONE);
|
|
||||||
android.widget.Toast.makeText(ExplorarRestaurantesActivity.this, "Erro ao carregar restaurantes.",
|
|
||||||
android.widget.Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupReservationOptions() {
|
private void setupReservationOptions() {
|
||||||
android.widget.Button btnDate = findViewById(R.id.btnSelectDate);
|
Button btnDate = findViewById(R.id.btnSelectDate);
|
||||||
android.widget.Button btnTime = findViewById(R.id.btnSelectTime);
|
Button btnTime = findViewById(R.id.btnSelectTime);
|
||||||
android.widget.Button btnVerAvaliacoes = findViewById(R.id.btnVerAvaliacoes);
|
Button btnVerAvaliacoes = findViewById(R.id.btnVerAvaliacoes);
|
||||||
android.widget.Button btnUploadFoto = findViewById(R.id.btnUploadFoto);
|
Button btnUploadFoto = findViewById(R.id.btnUploadFoto);
|
||||||
|
|
||||||
btnDate.setOnClickListener(v -> {
|
btnDate.setOnClickListener(v -> {
|
||||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
Calendar cal = Calendar.getInstance();
|
||||||
new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||||
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
||||||
btnDate.setText(selectedDate);
|
btnDate.setText(selectedDate);
|
||||||
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH),
|
}, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
|
||||||
cal.get(java.util.Calendar.DAY_OF_MONTH)).show();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
btnTime.setOnClickListener(v -> {
|
btnTime.setOnClickListener(v -> {
|
||||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
Calendar cal = Calendar.getInstance();
|
||||||
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
||||||
selectedTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
selectedTime = String.format(Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
||||||
btnTime.setText(selectedTime);
|
btnTime.setText(selectedTime);
|
||||||
}, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show();
|
}, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show();
|
||||||
});
|
});
|
||||||
|
|
||||||
findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> saveReservation());
|
findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> saveReservation());
|
||||||
|
|
||||||
btnVerAvaliacoes.setOnClickListener(v -> showReviewsDialog());
|
btnVerAvaliacoes.setOnClickListener(v -> showReviewsDialog());
|
||||||
|
|
||||||
btnUploadFoto.setOnClickListener(v -> {
|
btnUploadFoto.setOnClickListener(v -> {
|
||||||
android.content.Intent intent = new android.content.Intent(android.content.Intent.ACTION_PICK);
|
Intent intent = new Intent(Intent.ACTION_PICK);
|
||||||
intent.setType("image/*");
|
intent.setType("image/*");
|
||||||
photoPickerLauncher.launch(intent);
|
photoPickerLauncher.launch(intent);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadRestaurantPhoto(android.net.Uri imageUri) {
|
private void uploadRestaurantPhoto(android.net.Uri imageUri) {
|
||||||
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null)
|
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return;
|
||||||
return;
|
|
||||||
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
|
||||||
com.google.firebase.storage.StorageReference storageRef = com.google.firebase.storage.FirebaseStorage
|
|
||||||
.getInstance().getReference()
|
|
||||||
.child("restaurant_photos/" + encodedEmail + "/" + java.util.UUID.randomUUID().toString());
|
|
||||||
|
|
||||||
storageRef.putFile(imageUri).addOnSuccessListener(taskSnapshot -> {
|
// Simulating upload for now as Storage config might vary, but keeping logic
|
||||||
storageRef.getDownloadUrl().addOnSuccessListener(uri -> {
|
Toast.makeText(this, "Upload de foto em breve...", Toast.LENGTH_SHORT).show();
|
||||||
String photoUrl = uri.toString();
|
|
||||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("photos").child(encodedEmail)
|
|
||||||
.push().child("url").setValue(photoUrl).addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
android.widget.Toast.makeText(this, "Foto adicionada com sucesso!",
|
|
||||||
android.widget.Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}).addOnFailureListener(e -> {
|
|
||||||
android.widget.Toast.makeText(this, "Falha no upload: " + e.getMessage(), android.widget.Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showReviewsDialog() {
|
private void showReviewsDialog() {
|
||||||
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null)
|
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null) return;
|
||||||
return;
|
|
||||||
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
|
||||||
|
|
||||||
android.view.View dialogView = getLayoutInflater().inflate(R.layout.dialog_reviews_list, null);
|
View dialogView = getLayoutInflater().inflate(R.layout.dialog_reviews_list, null);
|
||||||
androidx.recyclerview.widget.RecyclerView rvReviews = dialogView.findViewById(R.id.rvReviewsList);
|
RecyclerView rvReviews = dialogView.findViewById(R.id.rvReviewsList);
|
||||||
android.widget.TextView txtEmpty = dialogView.findViewById(R.id.txtEmptyReviews);
|
TextView txtEmpty = dialogView.findViewById(R.id.txtEmptyReviews);
|
||||||
android.widget.Button btnClose = dialogView.findViewById(R.id.btnCloseReviews);
|
Button btnClose = dialogView.findViewById(R.id.btnCloseReviews);
|
||||||
android.widget.Button btnAdd = dialogView.findViewById(R.id.btnAddReview);
|
Button btnAdd = dialogView.findViewById(R.id.btnAddReview);
|
||||||
|
|
||||||
rvReviews.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(this));
|
rvReviews.setLayoutManager(new LinearLayoutManager(this));
|
||||||
|
|
||||||
androidx.appcompat.app.AlertDialog dialog = new androidx.appcompat.app.AlertDialog.Builder(this)
|
AlertDialog dialog = new AlertDialog.Builder(this)
|
||||||
.setView(dialogView)
|
.setView(dialogView)
|
||||||
.create();
|
.create();
|
||||||
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
if (dialog.getWindow() != null) dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||||
|
|
||||||
btnClose.setOnClickListener(v -> dialog.dismiss());
|
btnClose.setOnClickListener(v -> dialog.dismiss());
|
||||||
btnAdd.setOnClickListener(v -> {
|
btnAdd.setOnClickListener(v -> {
|
||||||
@@ -267,279 +237,152 @@ public class ExplorarRestaurantesActivity extends AppCompatActivity {
|
|||||||
addReviewDialog();
|
addReviewDialog();
|
||||||
});
|
});
|
||||||
|
|
||||||
com.google.firebase.database.DatabaseReference reviewsRef = com.google.firebase.database.FirebaseDatabase
|
String restId = selectedRestaurant.getId();
|
||||||
.getInstance()
|
db.collection("Reviews").whereEqualTo("restaurantId", restId).addSnapshotListener((value, error) -> {
|
||||||
.getReference("reviews").child(encodedEmail);
|
if (value == null) return;
|
||||||
|
List<Review> reviewsList = new ArrayList<>();
|
||||||
String currentUserEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null
|
for (QueryDocumentSnapshot doc : value) {
|
||||||
? com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
Review rev = doc.toObject(Review.class);
|
||||||
: null;
|
rev.setKey(doc.getId());
|
||||||
|
|
||||||
reviewsRef.addValueEventListener(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
java.util.List<com.example.pap_teste.models.Review> reviewsList = new java.util.ArrayList<>();
|
|
||||||
for (com.google.firebase.database.DataSnapshot dst : snapshot.getChildren()) {
|
|
||||||
String author = dst.child("author").getValue(String.class);
|
|
||||||
String text = dst.child("text").getValue(String.class);
|
|
||||||
String uEmail = dst.child("userEmail").getValue(String.class);
|
|
||||||
Float rating = dst.child("rating").getValue(Float.class);
|
|
||||||
|
|
||||||
if (rating == null)
|
|
||||||
rating = 0f;
|
|
||||||
|
|
||||||
com.example.pap_teste.models.Review rev = new com.example.pap_teste.models.Review(
|
|
||||||
dst.getKey(), author, text, rating, uEmail);
|
|
||||||
reviewsList.add(rev);
|
reviewsList.add(rev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reviewsList.isEmpty()) {
|
if (reviewsList.isEmpty()) {
|
||||||
txtEmpty.setVisibility(android.view.View.VISIBLE);
|
txtEmpty.setVisibility(View.VISIBLE);
|
||||||
rvReviews.setVisibility(android.view.View.GONE);
|
rvReviews.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
txtEmpty.setVisibility(android.view.View.GONE);
|
txtEmpty.setVisibility(View.GONE);
|
||||||
rvReviews.setVisibility(android.view.View.VISIBLE);
|
rvReviews.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String currentUserEmail = FirebaseAuth.getInstance().getCurrentUser() != null
|
||||||
|
? FirebaseAuth.getInstance().getCurrentUser().getEmail() : null;
|
||||||
|
|
||||||
ReviewAdapter adapter = new ReviewAdapter(reviewsList, currentUserEmail, review -> {
|
ReviewAdapter adapter = new ReviewAdapter(reviewsList, currentUserEmail, review -> {
|
||||||
// Confirmação de Apagar
|
new AlertDialog.Builder(this)
|
||||||
new androidx.appcompat.app.AlertDialog.Builder(ExplorarRestaurantesActivity.this)
|
|
||||||
.setTitle("Apagar Avaliação")
|
.setTitle("Apagar Avaliação")
|
||||||
.setMessage("Tens a certeza que queres apagar esta avaliação?")
|
.setMessage("Tens a certeza que queres apagar esta avaliação?")
|
||||||
.setPositiveButton("Sim", (d, w) -> {
|
.setPositiveButton("Sim", (d, w) -> deleteReview(review.getKey()))
|
||||||
deleteReview(review.getKey());
|
|
||||||
d.dismiss();
|
|
||||||
})
|
|
||||||
.setNegativeButton("Não", null)
|
.setNegativeButton("Não", null)
|
||||||
.show();
|
.show();
|
||||||
});
|
});
|
||||||
rvReviews.setAdapter(adapter);
|
rvReviews.setAdapter(adapter);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
android.widget.Toast.makeText(ExplorarRestaurantesActivity.this, "Erro ao carregar avaliações.",
|
|
||||||
android.widget.Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteReview(String reviewKey) {
|
private void deleteReview(String reviewKey) {
|
||||||
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null)
|
db.collection("Reviews").document(reviewKey).delete().addOnSuccessListener(aVoid -> {
|
||||||
return;
|
Snackbar.make(findViewById(R.id.explorarRoot), "Avaliação removida", Snackbar.LENGTH_SHORT).show();
|
||||||
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
recalculateRestaurantAverage(selectedRestaurant.getId());
|
||||||
|
|
||||||
com.google.firebase.database.DatabaseReference reviewsRef = com.google.firebase.database.FirebaseDatabase
|
|
||||||
.getInstance()
|
|
||||||
.getReference("reviews").child(encodedEmail);
|
|
||||||
|
|
||||||
reviewsRef.child(reviewKey).removeValue().addOnSuccessListener(aVoid -> {
|
|
||||||
com.google.android.material.snackbar.Snackbar.make(findViewById(R.id.explorarRoot), "Avaliação removida",
|
|
||||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
|
||||||
recalculateRestaurantAverage(encodedEmail);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void recalculateRestaurantAverage(String encodedEmail) {
|
private void recalculateRestaurantAverage(String restaurantId) {
|
||||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reviews").child(encodedEmail)
|
db.collection("Reviews").whereEqualTo("restaurantId", restaurantId).get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||||
.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(
|
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
double totalRating = 0;
|
double totalRating = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (com.google.firebase.database.DataSnapshot dst : snapshot.getChildren()) {
|
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||||
Float r = dst.child("rating").getValue(Float.class);
|
Double r = doc.getDouble("rating");
|
||||||
if (r != null) {
|
if (r != null) {
|
||||||
totalRating += r;
|
totalRating += r;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
double newAvg = count > 0 ? (totalRating / count) : 0.0;
|
double newAvg = count > 0 ? (totalRating / count) : 0.0;
|
||||||
|
Map<String, Object> updates = new HashMap<>();
|
||||||
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
|
||||||
updates.put("ratingAvg", newAvg);
|
updates.put("ratingAvg", newAvg);
|
||||||
updates.put("ratingCount", count);
|
updates.put("ratingCount", count);
|
||||||
|
db.collection("Restaurantes").document(restaurantId).update(updates);
|
||||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Restaurantes")
|
|
||||||
.child(encodedEmail).updateChildren(updates);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(
|
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addReviewDialog() {
|
private void addReviewDialog() {
|
||||||
if (selectedRestaurant == null || selectedRestaurant.getEmail() == null)
|
if (selectedRestaurant == null) return;
|
||||||
return;
|
|
||||||
|
|
||||||
android.view.View dialogView = getLayoutInflater().inflate(R.layout.dialog_leave_review, null);
|
View dialogView = getLayoutInflater().inflate(R.layout.dialog_leave_review, null);
|
||||||
com.example.pap_teste.components.InteractiveRatingBar ratingBar = dialogView
|
com.example.pap_teste.components.InteractiveRatingBar ratingBar = dialogView.findViewById(R.id.interactiveRatingBar);
|
||||||
.findViewById(R.id.interactiveRatingBar);
|
EditText input = dialogView.findViewById(R.id.etReviewComment);
|
||||||
android.widget.EditText input = dialogView.findViewById(R.id.etReviewComment);
|
Button btnSubmit = dialogView.findViewById(R.id.btnSubmitReview);
|
||||||
android.widget.Button btnSubmit = dialogView.findViewById(R.id.btnSubmitReview);
|
Button btnCancel = dialogView.findViewById(R.id.btnCancelReview);
|
||||||
android.widget.Button btnCancel = dialogView.findViewById(R.id.btnCancelReview);
|
|
||||||
|
|
||||||
btnSubmit.setEnabled(false);
|
btnSubmit.setEnabled(false);
|
||||||
btnSubmit.setAlpha(0.5f);
|
btnSubmit.setAlpha(0.5f);
|
||||||
|
|
||||||
input.addTextChangedListener(new android.text.TextWatcher() {
|
input.addTextChangedListener(new android.text.TextWatcher() {
|
||||||
@Override
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
boolean hasText = s != null && s.toString().trim().length() > 0;
|
boolean hasText = s != null && s.toString().trim().length() > 0;
|
||||||
btnSubmit.setEnabled(hasText);
|
btnSubmit.setEnabled(hasText);
|
||||||
btnSubmit.setAlpha(hasText ? 1.0f : 0.5f);
|
btnSubmit.setAlpha(hasText ? 1.0f : 0.5f);
|
||||||
}
|
}
|
||||||
|
@Override public void afterTextChanged(android.text.Editable s) {}
|
||||||
@Override
|
|
||||||
public void afterTextChanged(android.text.Editable s) {
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
androidx.appcompat.app.AlertDialog dialog = new androidx.appcompat.app.AlertDialog.Builder(this)
|
AlertDialog dialog = new AlertDialog.Builder(this).setView(dialogView).create();
|
||||||
.setView(dialogView)
|
if (dialog.getWindow() != null) dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||||
.create();
|
|
||||||
|
|
||||||
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
|
||||||
|
|
||||||
btnCancel.setOnClickListener(v -> dialog.dismiss());
|
btnCancel.setOnClickListener(v -> dialog.dismiss());
|
||||||
btnSubmit.setOnClickListener(v -> {
|
btnSubmit.setOnClickListener(v -> {
|
||||||
float rating = (float) ratingBar.getRating();
|
float rating = (float) ratingBar.getRating();
|
||||||
String revText = input.getText().toString().trim();
|
String revText = input.getText().toString().trim();
|
||||||
|
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
|
|
||||||
com.google.firebase.auth.FirebaseUser currentUser = com.google.firebase.auth.FirebaseAuth.getInstance()
|
String author = "Visitante";
|
||||||
.getCurrentUser();
|
String userEmail = user != null ? user.getEmail() : null;
|
||||||
if (currentUser != null && currentUser.getEmail() != null) {
|
|
||||||
String userDoc = currentUser.getEmail().replace(".", "_").replace("@", "_at_");
|
submitReviewToFirestore(author, revText, rating, userEmail);
|
||||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("Clientes").child(userDoc).get()
|
|
||||||
.addOnSuccessListener(snapshot -> {
|
|
||||||
String authorName = snapshot.exists()
|
|
||||||
&& snapshot.child("displayName").getValue(String.class) != null
|
|
||||||
? snapshot.child("displayName").getValue(String.class)
|
|
||||||
: "Visitante";
|
|
||||||
submitReviewToFirebase(authorName, revText, rating);
|
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}).addOnFailureListener(e -> {
|
|
||||||
submitReviewToFirebase("Visitante", revText, rating);
|
|
||||||
dialog.dismiss();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
submitReviewToFirebase("Visitante", revText, rating);
|
|
||||||
dialog.dismiss();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void submitReviewToFirebase(String authorName, String revText, float newRating) {
|
private void submitReviewToFirestore(String author, String text, float rating, String userEmail) {
|
||||||
String encodedEmail = selectedRestaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
Map<String, Object> review = new HashMap<>();
|
||||||
|
review.put("restaurantId", selectedRestaurant.getId());
|
||||||
|
review.put("author", author);
|
||||||
|
review.put("text", text);
|
||||||
|
review.put("rating", rating);
|
||||||
|
review.put("userEmail", userEmail);
|
||||||
|
review.put("timestamp", com.google.firebase.Timestamp.now());
|
||||||
|
|
||||||
com.google.firebase.auth.FirebaseUser currentUser = com.google.firebase.auth.FirebaseAuth.getInstance()
|
db.collection("Reviews").add(review).addOnSuccessListener(documentReference -> {
|
||||||
.getCurrentUser();
|
Snackbar.make(findViewById(R.id.explorarRoot), "Obrigado pela tua avaliação!", Snackbar.LENGTH_LONG).show();
|
||||||
String uEmail = currentUser != null ? currentUser.getEmail() : null;
|
recalculateRestaurantAverage(selectedRestaurant.getId());
|
||||||
|
|
||||||
// 1. Guardar a Review
|
|
||||||
java.util.Map<String, Object> review = new java.util.HashMap<>();
|
|
||||||
review.put("author", authorName);
|
|
||||||
review.put("text", revText);
|
|
||||||
review.put("rating", newRating);
|
|
||||||
review.put("userEmail", uEmail);
|
|
||||||
review.put("timestamp", com.google.firebase.database.ServerValue.TIMESTAMP);
|
|
||||||
|
|
||||||
com.google.firebase.database.FirebaseDatabase.getInstance().getReference("reviews")
|
|
||||||
.child(encodedEmail).push().setValue(review).addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
com.google.android.material.snackbar.Snackbar
|
|
||||||
.make(findViewById(R.id.explorarRoot), "Obrigado pela tua avaliação!",
|
|
||||||
com.google.android.material.snackbar.Snackbar.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 2. Atualizar Média no Restaurante
|
|
||||||
com.google.firebase.database.DatabaseReference restRef = com.google.firebase.database.FirebaseDatabase
|
|
||||||
.getInstance().getReference("Restaurantes").child(encodedEmail);
|
|
||||||
restRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
Double currentAvg = snapshot.child("ratingAvg").getValue(Double.class);
|
|
||||||
Integer currentCount = snapshot.child("ratingCount").getValue(Integer.class);
|
|
||||||
|
|
||||||
if (currentAvg == null)
|
|
||||||
currentAvg = 0.0;
|
|
||||||
if (currentCount == null)
|
|
||||||
currentCount = 0;
|
|
||||||
|
|
||||||
double newAvg = ((currentAvg * currentCount) + newRating) / (currentCount + 1);
|
|
||||||
|
|
||||||
java.util.Map<String, Object> updates = new java.util.HashMap<>();
|
|
||||||
updates.put("ratingAvg", newAvg);
|
|
||||||
updates.put("ratingCount", currentCount + 1);
|
|
||||||
|
|
||||||
restRef.updateChildren(updates);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveReservation() {
|
private void saveReservation() {
|
||||||
android.widget.EditText etPartySize = findViewById(R.id.etPartySize);
|
EditText etPartySize = findViewById(R.id.etPartySize);
|
||||||
int partySize = 0;
|
int partySize = 0;
|
||||||
try {
|
try {
|
||||||
partySize = Integer.parseInt(etPartySize.getText().toString());
|
partySize = Integer.parseInt(etPartySize.getText().toString());
|
||||||
} catch (Exception e) {
|
} catch (Exception ignored) {}
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedDate == null || selectedTime == null || partySize == 0) {
|
if (selectedDate == null || selectedTime == null || partySize == 0) {
|
||||||
android.widget.Toast.makeText(this, "Por favor, selecione data, hora e número de pessoas.",
|
Toast.makeText(this, "Por favor, selecione data, hora e número de pessoas.", Toast.LENGTH_SHORT).show();
|
||||||
android.widget.Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String userEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null
|
String userEmail = FirebaseAuth.getInstance().getCurrentUser() != null
|
||||||
? com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
? FirebaseAuth.getInstance().getCurrentUser().getEmail() : "cliente@teste.com";
|
||||||
: "cliente@teste.com";
|
|
||||||
|
|
||||||
com.google.firebase.database.DatabaseReference ref = com.google.firebase.database.FirebaseDatabase.getInstance()
|
Reserva reserva = new Reserva(
|
||||||
.getReference("reservas");
|
null,
|
||||||
String id = ref.push().getKey();
|
|
||||||
|
|
||||||
com.example.pap_teste.models.Reserva reserva = new com.example.pap_teste.models.Reserva(
|
|
||||||
id,
|
|
||||||
userEmail,
|
userEmail,
|
||||||
selectedRestaurant.getName(),
|
selectedRestaurant.getName(),
|
||||||
selectedRestaurant.getEmail(),
|
selectedRestaurant.getEmail(),
|
||||||
selectedDate,
|
selectedDate,
|
||||||
selectedTime,
|
selectedTime,
|
||||||
partySize,
|
partySize,
|
||||||
"Pendente");
|
"Pendente"
|
||||||
|
);
|
||||||
|
|
||||||
if (id != null) {
|
db.collection("Reservas").add(reserva).addOnSuccessListener(documentReference -> {
|
||||||
ref.child(id).setValue(reserva).addOnCompleteListener(task -> {
|
Toast.makeText(this, "Reserva solicitada com sucesso!", Toast.LENGTH_SHORT).show();
|
||||||
if (task.isSuccessful()) {
|
|
||||||
android.widget.Toast
|
|
||||||
.makeText(this, "Reserva solicitada com sucesso!", android.widget.Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
finish();
|
finish();
|
||||||
} else {
|
}).addOnFailureListener(e -> Toast.makeText(this, "Erro ao salvar reserva.", Toast.LENGTH_SHORT).show());
|
||||||
android.widget.Toast.makeText(this, "Erro ao salvar reserva.", android.widget.Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,51 @@
|
|||||||
package com.example.pap_teste;
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
import androidx.activity.EdgeToEdge;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.core.view.WindowInsetsCompat;
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.example.pap_teste.models.Restaurant;
|
import com.example.pap_teste.models.Restaurant;
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
import com.google.firebase.auth.FirebaseUser;
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
import com.google.firebase.database.DataSnapshot;
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
import com.google.firebase.database.DatabaseError;
|
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||||
import com.google.firebase.database.DatabaseReference;
|
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
|
||||||
import com.google.firebase.database.ValueEventListener;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class FavoritosActivity extends AppCompatActivity {
|
public class FavoritosActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private androidx.recyclerview.widget.RecyclerView rv;
|
private RecyclerView rv;
|
||||||
private RestaurantAdapter adapter;
|
private RestaurantAdapter adapter;
|
||||||
private List<Restaurant> list;
|
private List<Restaurant> list;
|
||||||
private android.widget.ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private android.view.View emptyState;
|
private View emptyState;
|
||||||
|
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
EdgeToEdge.enable(this);
|
||||||
setContentView(R.layout.activity_favoritos);
|
setContentView(R.layout.activity_favoritos);
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.favoritosRoot), (v, insets) -> {
|
|
||||||
|
View root = findViewById(R.id.favoritosRoot);
|
||||||
|
if (root != null) {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
return insets;
|
return insets;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Button back = findViewById(R.id.btnVoltar);
|
Button back = findViewById(R.id.btnVoltar);
|
||||||
if (back != null) {
|
if (back != null) {
|
||||||
@@ -54,8 +58,8 @@ public class FavoritosActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
list = new ArrayList<>();
|
list = new ArrayList<>();
|
||||||
adapter = new RestaurantAdapter(list, restaurant -> {
|
adapter = new RestaurantAdapter(list, restaurant -> {
|
||||||
android.content.Intent intent = new android.content.Intent(this, ExplorarRestaurantesActivity.class);
|
Intent intent = new Intent(this, RestaurantDetailActivity.class);
|
||||||
intent.putExtra("restaurant", restaurant);
|
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_ID, restaurant.getId());
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
});
|
});
|
||||||
rv.setAdapter(adapter);
|
rv.setAdapter(adapter);
|
||||||
@@ -65,35 +69,24 @@ public class FavoritosActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private void setupFavoritesList() {
|
private void setupFavoritesList() {
|
||||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
if (user != null && user.getEmail() != null) {
|
if (user != null) {
|
||||||
String encodedUserEmail = user.getEmail().replace(".", "_").replace("@", "_at_");
|
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||||
DatabaseReference favRef = FirebaseDatabase.getInstance().getReference("Clientes")
|
|
||||||
.child(encodedUserEmail).child("favorites");
|
db.collection("Clientes").document(user.getUid()).collection("favorites")
|
||||||
|
.addSnapshotListener((value, error) -> {
|
||||||
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
|
if (value == null) return;
|
||||||
|
|
||||||
favRef.addValueEventListener(new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
|
||||||
if (progressBar != null)
|
|
||||||
progressBar.setVisibility(android.view.View.GONE);
|
|
||||||
list.clear();
|
list.clear();
|
||||||
for (DataSnapshot ds : snapshot.getChildren()) {
|
for (QueryDocumentSnapshot doc : value) {
|
||||||
Restaurant restaurant = ds.getValue(Restaurant.class);
|
Restaurant restaurant = doc.toObject(Restaurant.class);
|
||||||
if (restaurant != null) {
|
restaurant.setId(doc.getId());
|
||||||
list.add(restaurant);
|
list.add(restaurant);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
|
|
||||||
if (emptyState != null) {
|
if (emptyState != null) {
|
||||||
emptyState.setVisibility(list.isEmpty() ? android.view.View.VISIBLE : android.view.View.GONE);
|
emptyState.setVisibility(list.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@NonNull DatabaseError error) {
|
|
||||||
if (progressBar != null)
|
|
||||||
progressBar.setVisibility(android.view.View.GONE);
|
|
||||||
Toast.makeText(FavoritosActivity.this, "Erro ao carregar favoritos.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,17 +12,15 @@ import androidx.recyclerview.widget.RecyclerView;
|
|||||||
import com.example.pap_teste.models.Restaurant;
|
import com.example.pap_teste.models.Restaurant;
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
import com.google.firebase.auth.FirebaseUser;
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
import com.google.firebase.database.DataSnapshot;
|
import com.google.firebase.firestore.DocumentReference;
|
||||||
import com.google.firebase.database.DatabaseError;
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
import com.google.firebase.database.DatabaseReference;
|
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
|
||||||
import com.google.firebase.database.ValueEventListener;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRestaurantAdapter.ViewHolder> {
|
public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRestaurantAdapter.ViewHolder> {
|
||||||
private List<Restaurant> restaurants;
|
private List<Restaurant> restaurants;
|
||||||
private RestaurantAdapter.OnRestaurantClickListener listener;
|
private RestaurantAdapter.OnRestaurantClickListener listener;
|
||||||
|
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
|
|
||||||
public FeaturedRestaurantAdapter(List<Restaurant> restaurants,
|
public FeaturedRestaurantAdapter(List<Restaurant> restaurants,
|
||||||
RestaurantAdapter.OnRestaurantClickListener listener) {
|
RestaurantAdapter.OnRestaurantClickListener listener) {
|
||||||
@@ -45,20 +43,17 @@ public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRest
|
|||||||
|
|
||||||
if (holder.txtRating != null) {
|
if (holder.txtRating != null) {
|
||||||
if (restaurant.getRatingAvg() != null && restaurant.getRatingAvg() > 0) {
|
if (restaurant.getRatingAvg() != null && restaurant.getRatingAvg() > 0) {
|
||||||
holder.txtRating
|
holder.txtRating.setText(String.format(java.util.Locale.getDefault(), "%.1f", restaurant.getRatingAvg()));
|
||||||
.setText(String.format(java.util.Locale.getDefault(), "%.1f", restaurant.getRatingAvg()));
|
|
||||||
} else {
|
} else {
|
||||||
holder.txtRating.setText("Novo");
|
holder.txtRating.setText("Novo");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (restaurant.getLogoUrl() != null && !restaurant.getLogoUrl().isEmpty()) {
|
if (restaurant.getImageURL() != null && !restaurant.getImageURL().isEmpty()) {
|
||||||
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
||||||
.load(restaurant.getLogoUrl())
|
.load(restaurant.getImageURL())
|
||||||
.centerCrop()
|
.centerCrop()
|
||||||
.into(holder.imgCover);
|
.into(holder.imgCover);
|
||||||
} else {
|
|
||||||
holder.imgCover.setImageResource(R.mipmap.ic_launcher);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.itemView.setOnClickListener(v -> {
|
holder.itemView.setOnClickListener(v -> {
|
||||||
@@ -69,49 +64,28 @@ public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRest
|
|||||||
|
|
||||||
if (holder.btnFavorite != null) {
|
if (holder.btnFavorite != null) {
|
||||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
if (user != null && user.getEmail() != null && restaurant.getEmail() != null) {
|
if (user != null && restaurant.getId() != null) {
|
||||||
String encodedUserEmail = user.getEmail().replace(".", "_").replace("@", "_at_");
|
DocumentReference favRef = db.collection("Clientes").document(user.getUid())
|
||||||
String encodedRestEmail = restaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
.collection("favorites").document(restaurant.getId());
|
||||||
DatabaseReference favRef = FirebaseDatabase.getInstance().getReference("Clientes")
|
|
||||||
.child(encodedUserEmail).child("favorites").child(encodedRestEmail);
|
|
||||||
|
|
||||||
// Read initial state
|
favRef.get().addOnSuccessListener(documentSnapshot -> {
|
||||||
favRef.addListenerForSingleValueEvent(new ValueEventListener() {
|
boolean exists = documentSnapshot.exists();
|
||||||
@Override
|
holder.btnFavorite.setTag(exists);
|
||||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
updateFavoriteUI(holder, exists);
|
||||||
holder.btnFavorite.setTag(snapshot.exists());
|
|
||||||
if (snapshot.exists()) {
|
|
||||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
|
||||||
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
|
||||||
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
|
||||||
} else {
|
|
||||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
|
||||||
holder.btnFavorite.clearColorFilter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@NonNull DatabaseError error) {
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
holder.btnFavorite.setOnClickListener(v -> {
|
holder.btnFavorite.setOnClickListener(v -> {
|
||||||
// Optimistic UI update
|
|
||||||
boolean isFav = holder.btnFavorite.getTag() != null && (Boolean) holder.btnFavorite.getTag();
|
boolean isFav = holder.btnFavorite.getTag() != null && (Boolean) holder.btnFavorite.getTag();
|
||||||
boolean newFavState = !isFav;
|
boolean newFavState = !isFav;
|
||||||
holder.btnFavorite.setTag(newFavState);
|
holder.btnFavorite.setTag(newFavState);
|
||||||
|
updateFavoriteUI(holder, newFavState);
|
||||||
|
|
||||||
if (newFavState) {
|
if (newFavState) {
|
||||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
favRef.set(restaurant);
|
||||||
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
|
||||||
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
|
||||||
favRef.setValue(restaurant);
|
|
||||||
com.google.android.material.snackbar.Snackbar.make(v, "Adicionado aos Favoritos!",
|
com.google.android.material.snackbar.Snackbar.make(v, "Adicionado aos Favoritos!",
|
||||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
||||||
} else {
|
} else {
|
||||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
favRef.delete();
|
||||||
holder.btnFavorite.clearColorFilter();
|
|
||||||
favRef.removeValue();
|
|
||||||
com.google.android.material.snackbar.Snackbar.make(v, "Removido dos Favoritos.",
|
com.google.android.material.snackbar.Snackbar.make(v, "Removido dos Favoritos.",
|
||||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
@@ -120,6 +94,17 @@ public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRest
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateFavoriteUI(ViewHolder holder, boolean isFav) {
|
||||||
|
if (isFav) {
|
||||||
|
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
||||||
|
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
||||||
|
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
||||||
|
} else {
|
||||||
|
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
||||||
|
holder.btnFavorite.clearColorFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return restaurants.size();
|
return restaurants.size();
|
||||||
@@ -132,10 +117,10 @@ public class FeaturedRestaurantAdapter extends RecyclerView.Adapter<FeaturedRest
|
|||||||
|
|
||||||
public ViewHolder(@NonNull View itemView) {
|
public ViewHolder(@NonNull View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
txtName = itemView.findViewById(R.id.txtFeaturedName);
|
txtName = itemView.findViewById(R.id.restaurantName);
|
||||||
txtCategory = itemView.findViewById(R.id.txtFeaturedCategory);
|
txtCategory = itemView.findViewById(R.id.restaurantCuisine);
|
||||||
txtRating = itemView.findViewById(R.id.txtFeaturedRating);
|
txtRating = null;
|
||||||
imgCover = itemView.findViewById(R.id.imgFeaturedCover);
|
imgCover = itemView.findViewById(R.id.restaurantImage);
|
||||||
btnFavorite = itemView.findViewById(R.id.btnFavorite);
|
btnFavorite = itemView.findViewById(R.id.btnFavorite);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
import com.google.firebase.firestore.CollectionReference;
|
||||||
|
|
||||||
|
public class FirestoreManager {
|
||||||
|
private static FirestoreManager instance;
|
||||||
|
private final FirebaseFirestore db;
|
||||||
|
|
||||||
|
private FirestoreManager() {
|
||||||
|
db = FirebaseFirestore.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized FirestoreManager getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new FirestoreManager();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FirebaseFirestore getDb() {
|
||||||
|
return db;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CollectionReference getUsersCollection() {
|
||||||
|
return db.collection("Clientes");
|
||||||
|
}
|
||||||
|
|
||||||
|
public CollectionReference getRestaurantsCollection() {
|
||||||
|
return db.collection("Restaurantes");
|
||||||
|
}
|
||||||
|
|
||||||
|
public CollectionReference getReservationsCollection() {
|
||||||
|
return db.collection("Reservas");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,12 @@ public class FoodCategoryAdapter extends RecyclerView.Adapter<FoodCategoryAdapte
|
|||||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
FoodCategory category = categories.get(position);
|
FoodCategory category = categories.get(position);
|
||||||
holder.txtName.setText(category.getName());
|
holder.txtName.setText(category.getName());
|
||||||
if (category.getImageResId() != 0) {
|
if (category.getImageUrl() != null && !category.getImageUrl().isEmpty()) {
|
||||||
|
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
||||||
|
.load(category.getImageUrl())
|
||||||
|
.centerCrop()
|
||||||
|
.into(holder.imgCategory);
|
||||||
|
} else if (category.getImageResId() != 0) {
|
||||||
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
||||||
.load(category.getImageResId())
|
.load(category.getImageResId())
|
||||||
.centerCrop()
|
.centerCrop()
|
||||||
|
|||||||
@@ -1,227 +0,0 @@
|
|||||||
package com.example.pap_teste;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
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.example.pap_teste.models.Mesa;
|
|
||||||
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 com.google.firebase.auth.FirebaseAuth;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
public class GerirMesasActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
private final List<Mesa> mesas = new ArrayList<>();
|
|
||||||
private ArrayAdapter<String> adapter;
|
|
||||||
private ListView listMesas;
|
|
||||||
private EditText inputNumero;
|
|
||||||
private EditText inputCapacidade;
|
|
||||||
private Spinner spinnerEstado;
|
|
||||||
private TextView txtMensagem;
|
|
||||||
private DatabaseReference mDatabase;
|
|
||||||
private Mesa selectedMesa = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
EdgeToEdge.enable(this);
|
|
||||||
setContentView(R.layout.activity_gerir_mesas);
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.gerirMesasRoot), (v, insets) -> {
|
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
|
||||||
return insets;
|
|
||||||
});
|
|
||||||
|
|
||||||
bindViews();
|
|
||||||
|
|
||||||
mDatabase = FirebaseDatabase.getInstance().getReference("Mesas");
|
|
||||||
|
|
||||||
setupList();
|
|
||||||
setupFormActions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindViews() {
|
|
||||||
listMesas = findViewById(R.id.listMesas);
|
|
||||||
inputNumero = findViewById(R.id.inputMesaNumero);
|
|
||||||
inputCapacidade = findViewById(R.id.inputMesaCapacidade);
|
|
||||||
spinnerEstado = findViewById(R.id.spinnerEstadoMesa);
|
|
||||||
txtMensagem = findViewById(R.id.txtMensagemMesa);
|
|
||||||
|
|
||||||
Button back = findViewById(R.id.btnVoltar);
|
|
||||||
if (back != null) {
|
|
||||||
back.setOnClickListener(v -> finish());
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayAdapter<String> estadoAdapter = new ArrayAdapter<>(
|
|
||||||
this,
|
|
||||||
android.R.layout.simple_spinner_dropdown_item,
|
|
||||||
new String[] { "Livre", "Ocupada", "Reservada" });
|
|
||||||
spinnerEstado.setAdapter(estadoAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupList() {
|
|
||||||
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1);
|
|
||||||
listMesas.setAdapter(adapter);
|
|
||||||
|
|
||||||
mDatabase.addValueEventListener(new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
|
||||||
mesas.clear();
|
|
||||||
adapter.clear();
|
|
||||||
String currentUserEmail = FirebaseAuth.getInstance().getCurrentUser() != null
|
|
||||||
? FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
|
||||||
: "";
|
|
||||||
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
|
|
||||||
Mesa mesa = postSnapshot.getValue(Mesa.class);
|
|
||||||
if (mesa != null && (mesa.getRestauranteEmail() == null || mesa.getRestauranteEmail().equals(currentUserEmail))) {
|
|
||||||
// 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());
|
|
||||||
adapter.add(resumo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@NonNull DatabaseError error) {
|
|
||||||
Toast.makeText(GerirMesasActivity.this, "Erro ao carregar mesas: " + error.getMessage(),
|
|
||||||
Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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()));
|
|
||||||
txtMensagem.setText(String.format("Editar mesa %d", item.getNumero()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getEstadoIndex(String estado) {
|
|
||||||
if ("Ocupada".equalsIgnoreCase(estado)) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if ("Reservada".equalsIgnoreCase(estado)) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupFormActions() {
|
|
||||||
Button btnGuardar = findViewById(R.id.btnGuardarMesa);
|
|
||||||
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() {
|
|
||||||
String numeroStr = inputNumero.getText().toString().trim();
|
|
||||||
String capacidadeStr = inputCapacidade.getText().toString().trim();
|
|
||||||
String estado = (String) spinnerEstado.getSelectedItem();
|
|
||||||
|
|
||||||
if (numeroStr.isEmpty() || capacidadeStr.isEmpty() || estado == null) {
|
|
||||||
Toast.makeText(this, "Preencha número, capacidade e estado.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numero;
|
|
||||||
int capacidade;
|
|
||||||
try {
|
|
||||||
numero = Integer.parseInt(numeroStr);
|
|
||||||
capacidade = Integer.parseInt(capacidadeStr);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
Toast.makeText(this, "Use apenas números válidos.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mesa existente = findMesa(numero);
|
|
||||||
String mesaId;
|
|
||||||
String currentUserEmail = FirebaseAuth.getInstance().getCurrentUser() != null
|
|
||||||
? FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
|
||||||
: "";
|
|
||||||
|
|
||||||
if (existente == null) {
|
|
||||||
mesaId = mDatabase.push().getKey();
|
|
||||||
Mesa novaMesa = new Mesa(mesaId, numero, capacidade, estado, currentUserEmail);
|
|
||||||
if (mesaId != null) {
|
|
||||||
mDatabase.child(mesaId).setValue(novaMesa);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
txtMensagem.setText(String.format("Mesa %d atualizada.", numero));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clearing inputs
|
|
||||||
limparCampos();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mesa findMesa(int numero) {
|
|
||||||
for (Mesa item : mesas) {
|
|
||||||
if (item.getNumero() == numero) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,295 +0,0 @@
|
|||||||
package com.example.pap_teste;
|
|
||||||
|
|
||||||
import com.example.pap_teste.models.Mesa;
|
|
||||||
import com.example.pap_teste.models.Staff;
|
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
|
||||||
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 androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.Button;
|
|
||||||
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
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 java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class GestaoStaffActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
private final List<Staff> staffList = new ArrayList<>();
|
|
||||||
private ArrayAdapter<String> staffAdapter;
|
|
||||||
private ListView listStaffMesas;
|
|
||||||
|
|
||||||
private Spinner spinnerNomeStaff;
|
|
||||||
private Spinner spinnerMesaStaff;
|
|
||||||
private TextView txtMensagemStaff;
|
|
||||||
|
|
||||||
private DatabaseReference staffRef;
|
|
||||||
private DatabaseReference mesasRef;
|
|
||||||
|
|
||||||
private List<String> staffNames = new ArrayList<>();
|
|
||||||
private List<Mesa> mesasDisponiveis = new ArrayList<>();
|
|
||||||
private ArrayAdapter<String> staffNameAdapter;
|
|
||||||
private ArrayAdapter<String> mesaSpinnerAdapter;
|
|
||||||
|
|
||||||
private FloatingActionButton floatingActionButton;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
EdgeToEdge.enable(this);
|
|
||||||
setContentView(R.layout.activity_gestao_staff);
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.gestaoStaffRoot), (v, insets) -> {
|
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
|
||||||
return insets;
|
|
||||||
});
|
|
||||||
|
|
||||||
staffRef = FirebaseDatabase.getInstance().getReference("Staff");
|
|
||||||
mesasRef = FirebaseDatabase.getInstance().getReference("Mesas");
|
|
||||||
|
|
||||||
Button back = findViewById(R.id.btnVoltar);
|
|
||||||
if (back != null) {
|
|
||||||
back.setOnClickListener(v -> finish());
|
|
||||||
}
|
|
||||||
|
|
||||||
bindViews();
|
|
||||||
setupMesaSpinner();
|
|
||||||
setupList();
|
|
||||||
setupFormActions();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindViews() {
|
|
||||||
listStaffMesas = findViewById(R.id.listStaffMesas);
|
|
||||||
spinnerNomeStaff = findViewById(R.id.spinnerNomeStaff);
|
|
||||||
spinnerMesaStaff = findViewById(R.id.spinnerMesaStaff);
|
|
||||||
txtMensagemStaff = findViewById(R.id.txtMensagemStaff);
|
|
||||||
floatingActionButton = findViewById(R.id.floatingActionButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preenche o spinner com uma lista simples de mesas (1–20).
|
|
||||||
* Mais tarde isto pode ser ligado às mesas reais configuradas em "Gerir Mesas".
|
|
||||||
*/
|
|
||||||
private void setupMesaSpinner() {
|
|
||||||
mesaSpinnerAdapter = new ArrayAdapter<>(
|
|
||||||
this,
|
|
||||||
android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
|
|
||||||
staffNameAdapter = new ArrayAdapter<>(
|
|
||||||
this,
|
|
||||||
android.R.layout.simple_spinner_dropdown_item,
|
|
||||||
staffNames);
|
|
||||||
spinnerNomeStaff.setAdapter(staffNameAdapter);
|
|
||||||
|
|
||||||
loadStaffMembers();
|
|
||||||
loadMesas();
|
|
||||||
|
|
||||||
spinnerMesaStaff.setAdapter(mesaSpinnerAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadMesas() {
|
|
||||||
mesasRef.addValueEventListener(new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
|
||||||
mesasDisponiveis.clear();
|
|
||||||
mesaSpinnerAdapter.clear();
|
|
||||||
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
|
|
||||||
Mesa mesa = postSnapshot.getValue(Mesa.class);
|
|
||||||
if (mesa != null) {
|
|
||||||
mesasDisponiveis.add(mesa);
|
|
||||||
mesaSpinnerAdapter.add("Mesa " + mesa.getNumero());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mesaSpinnerAdapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@NonNull DatabaseError error) {
|
|
||||||
Toast.makeText(GestaoStaffActivity.this, "Erro ao carregar mesas.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupList() {
|
|
||||||
staffAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1);
|
|
||||||
listStaffMesas.setAdapter(staffAdapter);
|
|
||||||
|
|
||||||
listStaffMesas.setOnItemClickListener((parent, view, position, id) -> {
|
|
||||||
Staff item = staffList.get(position);
|
|
||||||
// Select staff in spinner
|
|
||||||
int staffIndex = staffNames.indexOf(item.getName());
|
|
||||||
if (staffIndex >= 0) {
|
|
||||||
spinnerNomeStaff.setSelection(staffIndex);
|
|
||||||
}
|
|
||||||
// Select mesa in spinner
|
|
||||||
// Simple string matching for now since Mesa is stored as String in Staff
|
|
||||||
String assignedMesa = item.getMesa();
|
|
||||||
if (assignedMesa != null) {
|
|
||||||
for (int i = 0; i < mesaSpinnerAdapter.getCount(); i++) {
|
|
||||||
if (mesaSpinnerAdapter.getItem(i).equals(assignedMesa)) {
|
|
||||||
spinnerMesaStaff.setSelection(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
txtMensagemStaff.setText(String.format("A editar: %s", item.getName()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupFormActions() {
|
|
||||||
Button btnAtribuir = findViewById(R.id.btnAtribuirStaff);
|
|
||||||
if (btnAtribuir != null) {
|
|
||||||
btnAtribuir.setOnClickListener(v -> guardarAtribuicao());
|
|
||||||
}
|
|
||||||
|
|
||||||
Button btnEliminar = findViewById(R.id.btnEliminarStaff);
|
|
||||||
if (btnEliminar != null) {
|
|
||||||
btnEliminar.setOnClickListener(v -> eliminarStaff());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (floatingActionButton != null) {
|
|
||||||
floatingActionButton.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
Intent intent = new Intent(GestaoStaffActivity.this, AddStaffActivity.class);
|
|
||||||
startActivity(intent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
String nome = "";
|
|
||||||
if (spinnerNomeStaff.getSelectedItem() != null) {
|
|
||||||
nome = spinnerNomeStaff.getSelectedItem().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nome.isEmpty()) {
|
|
||||||
Toast.makeText(this, "Selecione um funcionário.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spinnerMesaStaff == null || spinnerMesaStaff.getSelectedItem() == null) {
|
|
||||||
Toast.makeText(this, "Selecione uma mesa.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String mesaSelecionada = spinnerMesaStaff.getSelectedItem().toString();
|
|
||||||
|
|
||||||
Staff staffToUpdate = findByNome(nome);
|
|
||||||
if (staffToUpdate != null) {
|
|
||||||
staffToUpdate.setMesa(mesaSelecionada);
|
|
||||||
|
|
||||||
final String finalNome = nome;
|
|
||||||
final String finalMesa = mesaSelecionada;
|
|
||||||
|
|
||||||
staffRef.child(staffToUpdate.getId()).setValue(staffToUpdate)
|
|
||||||
.addOnSuccessListener(aVoid -> {
|
|
||||||
txtMensagemStaff.setText(String.format("%s atribuído à %s.", finalNome, finalMesa));
|
|
||||||
})
|
|
||||||
.addOnFailureListener(e -> {
|
|
||||||
Toast.makeText(this, "Erro ao atualizar: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "Erro: Staff não encontrado.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void eliminarStaff() {
|
|
||||||
String nome = "";
|
|
||||||
if (spinnerNomeStaff.getSelectedItem() != null) {
|
|
||||||
nome = spinnerNomeStaff.getSelectedItem().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nome.isEmpty()) {
|
|
||||||
Toast.makeText(this, "Selecione um funcionário primeiro.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Staff staffToDelete = findByNome(nome);
|
|
||||||
if (staffToDelete != null) {
|
|
||||||
new androidx.appcompat.app.AlertDialog.Builder(this)
|
|
||||||
.setTitle("Eliminar Staff")
|
|
||||||
.setMessage("Tem a certeza que deseja eliminar o funcionário " + staffToDelete.getName() + "?")
|
|
||||||
.setPositiveButton("Eliminar", (dialog, which) -> {
|
|
||||||
staffRef.child(staffToDelete.getId()).removeValue()
|
|
||||||
.addOnSuccessListener(aVoid -> {
|
|
||||||
txtMensagemStaff.setText(staffToDelete.getName() + " foi eliminado.");
|
|
||||||
})
|
|
||||||
.addOnFailureListener(e -> {
|
|
||||||
Toast.makeText(this, "Erro ao eliminar: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.setNegativeButton("Cancelar", null)
|
|
||||||
.show();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "Staff não encontrado.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Staff findByNome(String nome) {
|
|
||||||
for (Staff item : staffList) {
|
|
||||||
if (item.getName().equalsIgnoreCase(nome)) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadStaffMembers() {
|
|
||||||
staffRef.addValueEventListener(new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
|
||||||
staffList.clear();
|
|
||||||
staffNames.clear();
|
|
||||||
staffAdapter.clear();
|
|
||||||
|
|
||||||
for (DataSnapshot postSnapshot : snapshot.getChildren()) {
|
|
||||||
Staff staff = postSnapshot.getValue(Staff.class);
|
|
||||||
if (staff != null && staff.getName() != null) {
|
|
||||||
staffList.add(staff);
|
|
||||||
staffNames.add(staff.getName());
|
|
||||||
|
|
||||||
String mesaInfo = staff.getMesa() != null ? staff.getMesa() : "Sem Mesa";
|
|
||||||
String resumo = String.format("%s • %s • %s", staff.getName(), staff.getZona(), mesaInfo);
|
|
||||||
staffAdapter.add(resumo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
staffNameAdapter.notifyDataSetChanged();
|
|
||||||
staffAdapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@NonNull DatabaseError error) {
|
|
||||||
Toast.makeText(GestaoStaffActivity.this, "Erro ao carregar staff.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
162
app/src/main/java/com/example/pap_teste/HomeFragment.java
Normal file
162
app/src/main/java/com/example/pap_teste/HomeFragment.java
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Restaurant;
|
||||||
|
import com.google.android.material.chip.Chip;
|
||||||
|
import com.google.android.material.chip.ChipGroup;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class HomeFragment extends Fragment {
|
||||||
|
|
||||||
|
private RecyclerView rvFeatured, rvNearYou;
|
||||||
|
private RestaurantAdapter featuredAdapter, nearYouAdapter;
|
||||||
|
private final List<Restaurant> allRestaurants = new ArrayList<>();
|
||||||
|
private final List<Restaurant> featuredList = new ArrayList<>();
|
||||||
|
private final List<Restaurant> nearYouList = new ArrayList<>();
|
||||||
|
private ChipGroup categoryChips;
|
||||||
|
private EditText etSearch;
|
||||||
|
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_home, container, false);
|
||||||
|
|
||||||
|
rvFeatured = view.findViewById(R.id.rvFeatured);
|
||||||
|
rvNearYou = view.findViewById(R.id.rvNearYou);
|
||||||
|
categoryChips = view.findViewById(R.id.categoryChips);
|
||||||
|
etSearch = view.findViewById(R.id.etSearch);
|
||||||
|
|
||||||
|
setupRecyclers();
|
||||||
|
loadCategories();
|
||||||
|
loadRestaurants();
|
||||||
|
setupSearch();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupRecyclers() {
|
||||||
|
featuredAdapter = new RestaurantAdapter(featuredList, true, this::onRestaurantClick);
|
||||||
|
rvFeatured.setAdapter(featuredAdapter);
|
||||||
|
rvFeatured.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||||
|
|
||||||
|
nearYouAdapter = new RestaurantAdapter(nearYouList, false, this::onRestaurantClick);
|
||||||
|
rvNearYou.setAdapter(nearYouAdapter);
|
||||||
|
rvNearYou.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCategories() {
|
||||||
|
db.collection("Categorias").get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||||
|
categoryChips.removeAllViews();
|
||||||
|
|
||||||
|
// Add "Tudo" chip
|
||||||
|
addCategoryChip("Tudo", true);
|
||||||
|
|
||||||
|
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||||
|
String name = doc.getString("name");
|
||||||
|
if (name != null) {
|
||||||
|
addCategoryChip(name, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryChips.setOnCheckedStateChangeListener((group, checkedIds) -> {
|
||||||
|
if (checkedIds.isEmpty()) {
|
||||||
|
filterRestaurants("Tudo");
|
||||||
|
} else {
|
||||||
|
Chip chip = group.findViewById(checkedIds.get(0));
|
||||||
|
filterRestaurants(chip.getText().toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCategoryChip(String name, boolean isSelected) {
|
||||||
|
Chip chip = (Chip) getLayoutInflater().inflate(R.layout.layout_filter_chip, categoryChips, false);
|
||||||
|
chip.setText(name);
|
||||||
|
chip.setChecked(isSelected);
|
||||||
|
categoryChips.addView(chip);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRestaurants() {
|
||||||
|
FirestoreManager.getInstance().getRestaurantsCollection().get().addOnCompleteListener(task -> {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
allRestaurants.clear();
|
||||||
|
for (QueryDocumentSnapshot document : task.getResult()) {
|
||||||
|
Restaurant r = document.toObject(Restaurant.class);
|
||||||
|
r.setId(document.getId());
|
||||||
|
allRestaurants.add(r);
|
||||||
|
}
|
||||||
|
filterRestaurants("Tudo");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupSearch() {
|
||||||
|
etSearch.addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
@Override public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
searchRestaurants(s.toString());
|
||||||
|
}
|
||||||
|
@Override public void afterTextChanged(Editable s) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void filterRestaurants(String category) {
|
||||||
|
featuredList.clear();
|
||||||
|
nearYouList.clear();
|
||||||
|
|
||||||
|
for (Restaurant r : allRestaurants) {
|
||||||
|
if (category.equals("Tudo") || category.equalsIgnoreCase(r.getCategory())) {
|
||||||
|
featuredList.add(r);
|
||||||
|
nearYouList.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
featuredAdapter.notifyDataSetChanged();
|
||||||
|
nearYouAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void searchRestaurants(String query) {
|
||||||
|
featuredList.clear();
|
||||||
|
nearYouList.clear();
|
||||||
|
|
||||||
|
for (Restaurant r : allRestaurants) {
|
||||||
|
if (r.getName().toLowerCase().contains(query.toLowerCase()) ||
|
||||||
|
r.getCategory().toLowerCase().contains(query.toLowerCase())) {
|
||||||
|
featuredList.add(r);
|
||||||
|
nearYouList.add(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
featuredAdapter.notifyDataSetChanged();
|
||||||
|
nearYouAdapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onRestaurantClick(Restaurant restaurant) {
|
||||||
|
Intent intent = new Intent(getActivity(), RestaurantDetailActivity.class);
|
||||||
|
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_ID, restaurant.getId());
|
||||||
|
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_NAME, restaurant.getName());
|
||||||
|
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_IMAGE, restaurant.getImageURL());
|
||||||
|
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_CUISINE, restaurant.getCategory());
|
||||||
|
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_RATING, restaurant.getRatingAvg());
|
||||||
|
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_DESC, restaurant.getDescription());
|
||||||
|
intent.putExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_ADDRESS, restaurant.getAddress());
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,250 +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;
|
|
||||||
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.example.pap_teste.models.Reserva;
|
|
||||||
import com.google.firebase.database.DataSnapshot;
|
|
||||||
import com.google.firebase.database.DatabaseError;
|
|
||||||
import com.google.firebase.database.DatabaseReference;
|
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
|
||||||
import com.google.firebase.database.ValueEventListener;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ListaEsperaActivity extends AppCompatActivity {
|
|
||||||
|
|
||||||
private final List<Reserva> reservasPendentes = new ArrayList<>();
|
|
||||||
private ArrayAdapter<String> adapter;
|
|
||||||
private ListView listReservas;
|
|
||||||
private TextView txtInfo, txtNotas, txtMensagem;
|
|
||||||
private Button btnConfirmar, btnRecusar;
|
|
||||||
private int selectedIndex = -1;
|
|
||||||
private String restaurantEmail;
|
|
||||||
private DatabaseReference databaseReference;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
EdgeToEdge.enable(this);
|
|
||||||
setContentView(R.layout.activity_lista_espera);
|
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.listaEsperaRoot), (v, insets) -> {
|
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
|
||||||
return insets;
|
|
||||||
});
|
|
||||||
|
|
||||||
restaurantEmail = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
|
||||||
if (restaurantEmail == null) {
|
|
||||||
if (com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null) {
|
|
||||||
restaurantEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail();
|
|
||||||
} else {
|
|
||||||
restaurantEmail = ""; // Avoid hardcoded sabor_arte so we don't accidentally load wrong data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bindViews();
|
|
||||||
setupList();
|
|
||||||
setupActions();
|
|
||||||
loadReservasPendentes();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindViews() {
|
|
||||||
listReservas = findViewById(R.id.listReservasP);
|
|
||||||
txtInfo = findViewById(R.id.txtReservaInfoP);
|
|
||||||
txtNotas = findViewById(R.id.txtReservaNotasP);
|
|
||||||
txtMensagem = findViewById(R.id.txtMensagemReservaP);
|
|
||||||
btnConfirmar = findViewById(R.id.btnConfirmarReservaP);
|
|
||||||
btnRecusar = findViewById(R.id.btnRecusarReservaP);
|
|
||||||
|
|
||||||
Button back = findViewById(R.id.btnVoltar);
|
|
||||||
if (back != null) {
|
|
||||||
back.setOnClickListener(v -> finish());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupList() {
|
|
||||||
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1);
|
|
||||||
listReservas.setAdapter(adapter);
|
|
||||||
|
|
||||||
listReservas.setOnItemClickListener((parent, view, position, id) -> {
|
|
||||||
selectedIndex = position;
|
|
||||||
mostrarDetalhe(reservasPendentes.get(position));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupActions() {
|
|
||||||
btnConfirmar.setOnClickListener(v -> mostrarMesasDisponiveis());
|
|
||||||
btnRecusar.setOnClickListener(v -> showRecusarDialog());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mostrarMesasDisponiveis() {
|
|
||||||
if (selectedIndex < 0 || selectedIndex >= reservasPendentes.size()) {
|
|
||||||
Toast.makeText(this, "Selecione uma reserva.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Reserva item = reservasPendentes.get(selectedIndex);
|
|
||||||
|
|
||||||
DatabaseReference mesasRef = FirebaseDatabase.getInstance().getReference("Mesas");
|
|
||||||
mesasRef.orderByChild("restauranteEmail").equalTo(restaurantEmail).addListenerForSingleValueEvent(new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) {
|
|
||||||
List<com.example.pap_teste.models.Mesa> 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")) {
|
|
||||||
m.setId(ds.getKey());
|
|
||||||
mesasLivres.add(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)?")
|
|
||||||
.setPositiveButton("Sim", (dialog, which) -> atualizarEstadoSelecionado("Confirmada"))
|
|
||||||
.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(ListaEsperaActivity.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 DatabaseError error) {
|
|
||||||
Toast.makeText(ListaEsperaActivity.this, "Erro ao carregar mesas.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void confirmarReservaComMesa(Reserva reserva, com.example.pap_teste.models.Mesa mesa) {
|
|
||||||
String novoEstado = "Confirmada (Mesa " + mesa.getNumero() + ")";
|
|
||||||
databaseReference.child(reserva.getId()).child("estado").setValue(novoEstado).addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
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();
|
|
||||||
selectedIndex = -1;
|
|
||||||
toggleButtons(null);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "Erro ao confirmar reserva.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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 atualizarEstadoSelecionado(String novoEstado) {
|
|
||||||
if (selectedIndex < 0 || selectedIndex >= reservasPendentes.size()) {
|
|
||||||
Toast.makeText(this, "Selecione uma reserva para avaliar.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Reserva item = reservasPendentes.get(selectedIndex);
|
|
||||||
databaseReference.child(item.getId()).child("estado").setValue(novoEstado).addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
Toast.makeText(this, "Reserva avaliada com sucesso.", Toast.LENGTH_SHORT).show();
|
|
||||||
selectedIndex = -1;
|
|
||||||
toggleButtons(null);
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "Erro ao alterar estado da reserva.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void mostrarDetalhe(Reserva item) {
|
|
||||||
txtInfo.setText(String.format("%s • %s", item.getClienteEmail(), item.getHora()));
|
|
||||||
txtNotas.setText(String.format("Data: %s • Pessoas: %d", item.getData(), item.getPessoas()));
|
|
||||||
toggleButtons(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void toggleButtons(Reserva item) {
|
|
||||||
if (item == null) {
|
|
||||||
btnConfirmar.setVisibility(android.view.View.GONE);
|
|
||||||
btnRecusar.setVisibility(android.view.View.GONE);
|
|
||||||
txtInfo.setText("Selecione uma reserva");
|
|
||||||
txtNotas.setText("");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
btnConfirmar.setVisibility(android.view.View.VISIBLE);
|
|
||||||
btnRecusar.setVisibility(android.view.View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadReservasPendentes() {
|
|
||||||
databaseReference = FirebaseDatabase.getInstance().getReference("reservas");
|
|
||||||
databaseReference.addValueEventListener(new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@androidx.annotation.NonNull DataSnapshot snapshot) {
|
|
||||||
reservasPendentes.clear();
|
|
||||||
String targetEmail = restaurantEmail != null ? restaurantEmail.trim() : "";
|
|
||||||
for (DataSnapshot data : snapshot.getChildren()) {
|
|
||||||
Reserva r = data.getValue(Reserva.class);
|
|
||||||
if (r != null && "Pendente".equals(r.getEstado())) {
|
|
||||||
String rEmail = r.getRestauranteEmail();
|
|
||||||
if (rEmail != null && rEmail.trim().equalsIgnoreCase(targetEmail)) {
|
|
||||||
reservasPendentes.add(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
refreshList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@androidx.annotation.NonNull DatabaseError error) {
|
|
||||||
Toast.makeText(ListaEsperaActivity.this, "Erro ao carregar lista de espera.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshList() {
|
|
||||||
adapter.clear();
|
|
||||||
for (Reserva item : reservasPendentes) {
|
|
||||||
adapter.add(String.format("%s - %dp • %s", item.getHora(), item.getPessoas(), item.getClienteEmail()));
|
|
||||||
}
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,587 +1,166 @@
|
|||||||
package com.example.pap_teste;
|
package com.example.pap_teste;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
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 android.os.Build;
|
|
||||||
import androidx.activity.result.ActivityResultLauncher;
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts;
|
|
||||||
|
|
||||||
import com.google.firebase.FirebaseApp;
|
import com.example.pap_teste.models.User;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
import com.google.android.material.button.MaterialButtonToggleGroup;
|
||||||
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
import com.google.android.material.textfield.TextInputLayout;
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
import com.google.firebase.database.DataSnapshot;
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
import com.google.firebase.database.DatabaseReference;
|
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
public static final String EXTRA_ACTION_MODE = "extra_action_mode";
|
|
||||||
public static final String EXTRA_DISPLAY_NAME = "extra_display_name";
|
public static final String EXTRA_DISPLAY_NAME = "extra_display_name";
|
||||||
public static final String EXTRA_EMAIL = "extra_email";
|
public static final String EXTRA_EMAIL = "extra_email";
|
||||||
public static final String EXTRA_ACCOUNT_TYPE = "extra_account_type";
|
public static final String EXTRA_ACCOUNT_TYPE = "extra_account_type";
|
||||||
public static final String EXTRA_ROLE = "extra_role";
|
public static final String EXTRA_ROLE = "extra_role";
|
||||||
private static final String PREFS_NAME = "pap_prefs";
|
public static final String EXTRA_ACTION_MODE = "extra_action_mode";
|
||||||
private static final String KEY_HAS_CREATED_ACCOUNT = "has_created_account";
|
public enum AccountAction { CRIAR, ENTRAR }
|
||||||
|
|
||||||
public enum AccountType {
|
private FirebaseAuth mAuth;
|
||||||
CLIENTE, ESTABELECIMENTO
|
private FirebaseFirestore db;
|
||||||
}
|
private boolean isLoginMode = true;
|
||||||
|
|
||||||
public enum AccountAction {
|
private TextInputLayout layoutName;
|
||||||
ENTRAR, CRIAR
|
private TextInputEditText inputName, inputEmail, inputPassword;
|
||||||
}
|
private MaterialButton btnPrimaryAction;
|
||||||
|
private ProgressBar progressBar;
|
||||||
private AccountType selectedAccountType = AccountType.CLIENTE;
|
|
||||||
private AccountAction selectedAccountAction = AccountAction.ENTRAR;
|
|
||||||
private Button btnEntrar;
|
|
||||||
private Button btnCriarConta;
|
|
||||||
private Button btnPrimaryAction;
|
|
||||||
private EditText inputName;
|
|
||||||
private EditText inputEmail;
|
|
||||||
private EditText inputPassword;
|
|
||||||
private EditText inputOwnerPhone;
|
|
||||||
private EditText inputEstablishmentName;
|
|
||||||
private EditText inputEstablishmentEmail;
|
|
||||||
private EditText inputEstablishmentPhone;
|
|
||||||
private android.widget.TextView txtForgotPassword;
|
|
||||||
private android.widget.ImageView iconPasswordVisibility;
|
|
||||||
private boolean isPasswordVisible = false;
|
|
||||||
private boolean hasCreatedAccount;
|
|
||||||
private FirebaseAuth firebaseAuth;
|
|
||||||
private DatabaseReference databaseReference;
|
|
||||||
|
|
||||||
private final ActivityResultLauncher<String[]> permissionRequest = registerForActivityResult(
|
|
||||||
new ActivityResultContracts.RequestMultiplePermissions(), result -> {
|
|
||||||
Boolean fineLocationGranted = result.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false);
|
|
||||||
Boolean bluetoothScanGranted = true;
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
||||||
bluetoothScanGranted = result.getOrDefault(Manifest.permission.BLUETOOTH_SCAN, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fineLocationGranted != null && fineLocationGranted) {
|
|
||||||
// Precise location access granted.
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "A permissão de localização é necessária para o check-in.", Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
|
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
|
||||||
return insets;
|
|
||||||
});
|
|
||||||
|
|
||||||
selectedAccountType = AccountType.CLIENTE;
|
mAuth = FirebaseAuth.getInstance();
|
||||||
|
db = FirebaseFirestore.getInstance();
|
||||||
|
progressBar = findViewById(R.id.progressBarLogin);
|
||||||
|
|
||||||
FirebaseApp.initializeApp(this);
|
if (mAuth.getCurrentUser() != null) {
|
||||||
firebaseAuth = FirebaseAuth.getInstance();
|
checkUserRoleAndRedirect(mAuth.getCurrentUser().getUid());
|
||||||
databaseReference = FirebaseDatabase.getInstance().getReference();
|
|
||||||
|
|
||||||
bindViews();
|
|
||||||
setupActionToggle();
|
|
||||||
setupPrimaryAction();
|
|
||||||
checkPermissions();
|
|
||||||
migrateUsersNodeToSeparateCollections();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void migrateUsersNodeToSeparateCollections() {
|
|
||||||
if (databaseReference == null) return;
|
|
||||||
databaseReference.child("users").get().addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful() && task.getResult() != null && task.getResult().exists()) {
|
|
||||||
for (DataSnapshot userSnapshot : task.getResult().getChildren()) {
|
|
||||||
String accountType = userSnapshot.child("accountType").getValue(String.class);
|
|
||||||
String role = userSnapshot.child("role").getValue(String.class);
|
|
||||||
|
|
||||||
String targetCollection = "Clientes";
|
|
||||||
if ("ESTABELECIMENTO".equalsIgnoreCase(accountType) || "ADMIN".equalsIgnoreCase(role)) {
|
|
||||||
targetCollection = "Restaurantes";
|
|
||||||
}
|
|
||||||
|
|
||||||
databaseReference.child(targetCollection).child(userSnapshot.getKey())
|
|
||||||
.setValue(userSnapshot.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkPermissions() {
|
|
||||||
List<String> permissionsNeeded = new ArrayList<>();
|
|
||||||
if (ContextCompat.checkSelfPermission(this,
|
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
permissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
||||||
if (ContextCompat.checkSelfPermission(this,
|
|
||||||
Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
permissionsNeeded.add(Manifest.permission.BLUETOOTH_SCAN);
|
|
||||||
}
|
|
||||||
if (ContextCompat.checkSelfPermission(this,
|
|
||||||
Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
permissionsNeeded.add(Manifest.permission.BLUETOOTH_CONNECT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
||||||
if (ContextCompat.checkSelfPermission(this,
|
|
||||||
Manifest.permission.READ_MEDIA_IMAGES) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
permissionsNeeded.add(Manifest.permission.READ_MEDIA_IMAGES);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (ContextCompat.checkSelfPermission(this,
|
initViews();
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
|
||||||
permissionsNeeded.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!permissionsNeeded.isEmpty()) {
|
private void initViews() {
|
||||||
new AlertDialog.Builder(this)
|
layoutName = findViewById(R.id.layoutName);
|
||||||
.setTitle("Permissões Necessárias")
|
|
||||||
.setMessage(
|
|
||||||
"Para o correto funcionamento do check-in, serviços de proximidade e fotos da galeria, precisamos de algumas permissões.")
|
|
||||||
.setPositiveButton("Configurar", (dialog, which) -> {
|
|
||||||
permissionRequest.launch(permissionsNeeded.toArray(new String[0]));
|
|
||||||
})
|
|
||||||
.setNegativeButton("Agora Não", null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bindViews() {
|
|
||||||
btnEntrar = findViewById(R.id.btnEntrar);
|
|
||||||
btnCriarConta = findViewById(R.id.btnCriarConta);
|
|
||||||
btnPrimaryAction = findViewById(R.id.btnFinalCriarConta);
|
|
||||||
inputName = findViewById(R.id.inputName);
|
inputName = findViewById(R.id.inputName);
|
||||||
inputEmail = findViewById(R.id.inputEmail);
|
inputEmail = findViewById(R.id.inputEmail);
|
||||||
inputPassword = findViewById(R.id.inputPassword);
|
inputPassword = findViewById(R.id.inputPassword);
|
||||||
inputOwnerPhone = findViewById(R.id.inputOwnerPhone);
|
btnPrimaryAction = findViewById(R.id.btnPrimaryAction);
|
||||||
inputEstablishmentName = findViewById(R.id.inputEstablishmentName);
|
MaterialButtonToggleGroup toggleGroup = findViewById(R.id.toggleGroup);
|
||||||
inputEstablishmentEmail = findViewById(R.id.inputEstablishmentEmail);
|
|
||||||
inputEstablishmentPhone = findViewById(R.id.inputEstablishmentPhone);
|
|
||||||
txtForgotPassword = findViewById(R.id.txtForgotPassword);
|
|
||||||
iconPasswordVisibility = findViewById(R.id.iconPasswordVisibility);
|
|
||||||
|
|
||||||
setupPasswordFeatures();
|
toggleGroup.addOnButtonCheckedListener((group, checkedId, isChecked) -> {
|
||||||
|
if (isChecked) {
|
||||||
|
isLoginMode = checkedId == R.id.btnEntrar;
|
||||||
|
layoutName.setVisibility(isLoginMode ? View.GONE : View.VISIBLE);
|
||||||
|
btnPrimaryAction.setText(isLoginMode ? "Entrar" : "Criar Conta");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
btnPrimaryAction.setOnClickListener(v -> handleAuth());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPasswordFeatures() {
|
private void checkUserRoleAndRedirect(String uid) {
|
||||||
iconPasswordVisibility.setOnClickListener(v -> {
|
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||||
isPasswordVisible = !isPasswordVisible;
|
|
||||||
if (isPasswordVisible) {
|
// 1. Try Clientes
|
||||||
inputPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT
|
db.collection("Clientes").document(uid).get().addOnSuccessListener(docClient -> {
|
||||||
| android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
|
if (docClient.exists()) {
|
||||||
iconPasswordVisibility.setImageResource(R.drawable.ic_visibility);
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
|
startActivity(new Intent(this, ClientDashboardActivity.class));
|
||||||
|
finish();
|
||||||
} else {
|
} else {
|
||||||
inputPassword.setInputType(
|
// 2. Try legacy users collection
|
||||||
android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
db.collection("users").document(uid).get().addOnSuccessListener(docLegacy -> {
|
||||||
iconPasswordVisibility.setImageResource(R.drawable.ic_visibility_off);
|
if (docLegacy.exists()) {
|
||||||
}
|
// Migrate to Clientes
|
||||||
inputPassword.setSelection(inputPassword.getText().length());
|
User legacyUser = docLegacy.toObject(User.class);
|
||||||
|
if (legacyUser != null && "CLIENTE".equalsIgnoreCase(legacyUser.getRole())) {
|
||||||
|
db.collection("Clientes").document(uid).set(legacyUser)
|
||||||
|
.addOnSuccessListener(aVoid -> {
|
||||||
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
|
startActivity(new Intent(this, ClientDashboardActivity.class));
|
||||||
|
finish();
|
||||||
});
|
});
|
||||||
|
|
||||||
txtForgotPassword.setOnClickListener(v -> {
|
|
||||||
String email = inputEmail.getText().toString().trim();
|
|
||||||
if (TextUtils.isEmpty(email)) {
|
|
||||||
Toast.makeText(this, "Por favor, introduza o seu email primeiro.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
new AlertDialog.Builder(this)
|
|
||||||
.setTitle("Recuperar palavra-passe")
|
|
||||||
.setMessage("Deseja enviar um email de recuperação para " + email + "?")
|
|
||||||
.setPositiveButton("Sim", (dialog, which) -> {
|
|
||||||
if (firebaseAuth != null) {
|
|
||||||
firebaseAuth.sendPasswordResetEmail(email)
|
|
||||||
.addOnCompleteListener(task -> {
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
Toast.makeText(MainActivity.this, "Email de recuperação enviado!",
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(MainActivity.this,
|
handleNoClientAccess(uid);
|
||||||
"Falha ao enviar email. Verifique se o endereço está correto.",
|
}
|
||||||
Toast.LENGTH_LONG).show();
|
} else {
|
||||||
|
handleNoClientAccess(uid);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
}).addOnFailureListener(e -> {
|
||||||
.setNegativeButton("Não", null)
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
.show();
|
Toast.makeText(this, "Erro de rede ou permissões. Verifique a sua ligação.", Toast.LENGTH_LONG).show();
|
||||||
|
initViews();
|
||||||
|
});
|
||||||
|
initViews();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNoClientAccess(String uid) {
|
||||||
|
// Check if it's a Restaurant
|
||||||
|
db.collection("Restaurantes").document(uid).get().addOnSuccessListener(docRest -> {
|
||||||
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
|
if (docRest.exists()) {
|
||||||
|
Toast.makeText(this, "Contas de Restaurante devem usar o Dashboard Web.", Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, "Utilizador não encontrado. Por favor, crie uma conta.", Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
mAuth.signOut();
|
||||||
|
initViews();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleAuth() {
|
||||||
private void setupActionToggle() {
|
|
||||||
btnEntrar.setOnClickListener(v -> {
|
|
||||||
selectedAccountAction = AccountAction.ENTRAR;
|
|
||||||
updateActionButtons();
|
|
||||||
});
|
|
||||||
btnCriarConta.setOnClickListener(v -> {
|
|
||||||
selectedAccountAction = AccountAction.CRIAR;
|
|
||||||
updateActionButtons();
|
|
||||||
});
|
|
||||||
updateActionButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupPrimaryAction() {
|
|
||||||
btnPrimaryAction.setOnClickListener(v -> handlePrimaryAction());
|
|
||||||
updatePrimaryActionState();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void updateActionButtons() {
|
|
||||||
setSelectedState(btnEntrar, selectedAccountAction == AccountAction.ENTRAR);
|
|
||||||
setSelectedState(btnCriarConta, selectedAccountAction == AccountAction.CRIAR);
|
|
||||||
updatePrimaryActionState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSelectedState(Button button, boolean isSelected) {
|
|
||||||
int selectedTextColor = Color.WHITE;
|
|
||||||
int defaultTextColor = Color.parseColor("#231F1F");
|
|
||||||
button.setBackgroundResource(isSelected ? R.drawable.tab_selected : R.drawable.tab_unselected);
|
|
||||||
button.setTextColor(isSelected ? selectedTextColor : defaultTextColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updatePrimaryActionState() {
|
|
||||||
boolean creatingAccount = selectedAccountAction == AccountAction.CRIAR;
|
|
||||||
btnPrimaryAction.setText(creatingAccount ? "Criar Conta" : "Entrar");
|
|
||||||
updateInputVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateInputVisibility() {
|
|
||||||
boolean creatingAccount = selectedAccountAction == AccountAction.CRIAR;
|
|
||||||
boolean isEstablishment = selectedAccountType == AccountType.ESTABELECIMENTO;
|
|
||||||
|
|
||||||
inputName.setHint(isEstablishment ? "Nome do proprietário" : "O seu nome");
|
|
||||||
inputEmail.setHint(isEstablishment ? "Email do proprietário" : "Email");
|
|
||||||
|
|
||||||
inputName.setVisibility(creatingAccount ? View.VISIBLE : View.GONE);
|
|
||||||
inputOwnerPhone.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE);
|
|
||||||
inputEstablishmentName.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE);
|
|
||||||
inputEstablishmentEmail.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE);
|
|
||||||
inputEstablishmentPhone.setVisibility(creatingAccount && isEstablishment ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
if (txtForgotPassword != null) {
|
|
||||||
txtForgotPassword.setVisibility(creatingAccount ? View.GONE : View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handlePrimaryAction() {
|
|
||||||
String email = inputEmail.getText().toString().trim();
|
String email = inputEmail.getText().toString().trim();
|
||||||
String password = inputPassword.getText().toString().trim();
|
String password = inputPassword.getText().toString().trim();
|
||||||
String providedName = inputName.getText().toString().trim();
|
String name = inputName.getText().toString().trim();
|
||||||
String ownerPhone = inputOwnerPhone.getText().toString().trim();
|
|
||||||
String establishmentName = inputEstablishmentName.getText().toString().trim();
|
|
||||||
String establishmentEmail = inputEstablishmentEmail.getText().toString().trim();
|
|
||||||
String establishmentPhone = inputEstablishmentPhone.getText().toString().trim();
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(email) || TextUtils.isEmpty(password)) {
|
if (TextUtils.isEmpty(email) || TextUtils.isEmpty(password)) {
|
||||||
Toast.makeText(this, "Preencha email e palavra-passe.", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "Preencha todos os campos.", Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean creatingAccount = selectedAccountAction == AccountAction.CRIAR;
|
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
if (creatingAccount && !isValidPassword(password)) {
|
if (isLoginMode) {
|
||||||
return;
|
mAuth.signInWithEmailAndPassword(email, password)
|
||||||
}
|
.addOnSuccessListener(authResult -> checkUserRoleAndRedirect(authResult.getUser().getUid()))
|
||||||
|
|
||||||
if (creatingAccount) {
|
|
||||||
if (selectedAccountType == AccountType.CLIENTE && TextUtils.isEmpty(providedName)) {
|
|
||||||
Toast.makeText(this, "Indique o seu nome para criar conta.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedAccountType == AccountType.ESTABELECIMENTO) {
|
|
||||||
boolean missingOwner = TextUtils.isEmpty(providedName) || TextUtils.isEmpty(ownerPhone);
|
|
||||||
boolean missingEstablishment = TextUtils.isEmpty(establishmentName)
|
|
||||||
|| TextUtils.isEmpty(establishmentEmail)
|
|
||||||
|| TextUtils.isEmpty(establishmentPhone);
|
|
||||||
|
|
||||||
if (missingOwner || missingEstablishment) {
|
|
||||||
Toast.makeText(this, "Preencha os dados do proprietário e do estabelecimento.", Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String fallbackName = !TextUtils.isEmpty(providedName) ? providedName : deriveNameFromEmail(email);
|
|
||||||
String resolvedRole = getRoleForSelectedType();
|
|
||||||
|
|
||||||
if (creatingAccount) {
|
|
||||||
createAccountInFirebase(
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
providedName,
|
|
||||||
ownerPhone,
|
|
||||||
establishmentName,
|
|
||||||
establishmentEmail,
|
|
||||||
establishmentPhone,
|
|
||||||
fallbackName,
|
|
||||||
resolvedRole);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
signInExistingAccount(email, password, fallbackName, resolvedRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String deriveNameFromEmail(String email) {
|
|
||||||
if (!email.contains("@")) {
|
|
||||||
return "Utilizador";
|
|
||||||
}
|
|
||||||
String candidate = email.substring(0, email.indexOf("@"));
|
|
||||||
if (candidate.isEmpty()) {
|
|
||||||
return "Utilizador";
|
|
||||||
}
|
|
||||||
String firstLetter = candidate.substring(0, 1).toUpperCase();
|
|
||||||
String rest = candidate.length() > 1 ? candidate.substring(1) : "";
|
|
||||||
return firstLetter + rest;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void markAccountCreated() {
|
|
||||||
hasCreatedAccount = true;
|
|
||||||
SharedPreferences prefs = getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
|
|
||||||
prefs.edit().putBoolean(KEY_HAS_CREATED_ACCOUNT, true).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getRoleForSelectedType() {
|
|
||||||
return selectedAccountType == AccountType.ESTABELECIMENTO ? "ADMIN" : "CLIENTE";
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildDocumentId(String email) {
|
|
||||||
return email.replace(".", "_").replace("@", "_at_");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean ensureFirebaseReady() {
|
|
||||||
boolean ready = firebaseAuth != null && databaseReference != null;
|
|
||||||
if (!ready) {
|
|
||||||
Toast.makeText(this, "Ligue-se ao Firebase para continuar.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
return ready;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void signInExistingAccount(String email, String password, String fallbackName, String resolvedRole) {
|
|
||||||
if (!ensureFirebaseReady()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
firebaseAuth.signInWithEmailAndPassword(email, password)
|
|
||||||
.addOnSuccessListener(authResult -> fetchAccountAndNavigate(email, fallbackName, resolvedRole))
|
|
||||||
.addOnFailureListener(e -> {
|
.addOnFailureListener(e -> {
|
||||||
android.util.Log.e("LoginError", "SignIn failed", e);
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
Toast.makeText(this, "Não foi possível iniciar sessão: " + e.getMessage(), Toast.LENGTH_SHORT)
|
Toast.makeText(this, "Login falhou: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
.show();
|
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
|
if (TextUtils.isEmpty(name)) {
|
||||||
private void createAccountInFirebase(
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
String email,
|
Toast.makeText(this, "Insira o seu nome.", Toast.LENGTH_SHORT).show();
|
||||||
String password,
|
|
||||||
String providedName,
|
|
||||||
String ownerPhone,
|
|
||||||
String establishmentName,
|
|
||||||
String establishmentEmail,
|
|
||||||
String establishmentPhone,
|
|
||||||
String fallbackName,
|
|
||||||
String resolvedRole) {
|
|
||||||
if (!ensureFirebaseReady()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mAuth.createUserWithEmailAndPassword(email, password)
|
||||||
firebaseAuth.createUserWithEmailAndPassword(email, password)
|
.addOnSuccessListener(authResult -> {
|
||||||
.addOnSuccessListener(result -> {
|
String uid = authResult.getUser().getUid();
|
||||||
if (!hasCreatedAccount) {
|
User newUser = new User(uid, name, email, "CLIENTE");
|
||||||
markAccountCreated();
|
db.collection("Clientes").document(uid).set(newUser)
|
||||||
}
|
.addOnSuccessListener(aVoid -> {
|
||||||
String finalDisplayName = selectedAccountType == AccountType.ESTABELECIMENTO
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
&& !TextUtils.isEmpty(establishmentName)
|
startActivity(new Intent(this, ClientDashboardActivity.class));
|
||||||
? establishmentName
|
finish();
|
||||||
: fallbackName;
|
|
||||||
String uid = result.getUser() != null ? result.getUser().getUid() : null;
|
|
||||||
|
|
||||||
persistAccountInFirebase(
|
|
||||||
email,
|
|
||||||
providedName,
|
|
||||||
resolvedRole,
|
|
||||||
ownerPhone,
|
|
||||||
establishmentName,
|
|
||||||
establishmentEmail,
|
|
||||||
establishmentPhone,
|
|
||||||
uid,
|
|
||||||
() -> {
|
|
||||||
Toast.makeText(this, "Conta criada com sucesso! Carregue em Entrar.", Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
selectedAccountAction = AccountAction.ENTRAR;
|
|
||||||
updateActionButtons();
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.addOnFailureListener(e -> {
|
.addOnFailureListener(e -> {
|
||||||
android.util.Log.e("LoginError", "CreateUser failed", e);
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
Toast.makeText(this, "Falha ao criar conta: " + e.getMessage(), Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "Registo falhou: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchAccountAndNavigate(String email, String fallbackName, String resolvedRole) {
|
|
||||||
if (databaseReference == null) {
|
|
||||||
Toast.makeText(this, "Firebase indisponível.", Toast.LENGTH_SHORT).show();
|
|
||||||
if (firebaseAuth != null) firebaseAuth.signOut();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String documentId = buildDocumentId(email);
|
|
||||||
String collectionPath = selectedAccountType == AccountType.ESTABELECIMENTO ? "Restaurantes" : "Clientes";
|
|
||||||
databaseReference.child(collectionPath).child(documentId).get().addOnCompleteListener(task -> {
|
|
||||||
if (!task.isSuccessful()) {
|
|
||||||
android.util.Log.e("LoginError", "Database check failed", task.getException());
|
|
||||||
Toast.makeText(this, "Falha ao validar perfil. Tente novamente.", Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
if (firebaseAuth != null) firebaseAuth.signOut();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataSnapshot snapshot = task.getResult();
|
|
||||||
if (snapshot == null || !snapshot.exists()) {
|
|
||||||
String tipo = selectedAccountType == AccountType.ESTABELECIMENTO ? "Estabelecimento" : "Cliente";
|
|
||||||
Toast.makeText(this, "Conta não encontrada como " + tipo + ".\nPor favor, crie uma conta nova.", Toast.LENGTH_LONG).show();
|
|
||||||
if (firebaseAuth != null) firebaseAuth.signOut();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String accountTypeInFirebase = snapshot.child("accountType").getValue(String.class);
|
|
||||||
if (accountTypeInFirebase != null
|
|
||||||
&& !accountTypeInFirebase.equalsIgnoreCase(selectedAccountType.name())) {
|
|
||||||
Toast.makeText(this, "Tipo de conta não corresponde ao registo.\nPor favor verifique se escolheu a opção correta.", Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
if (firebaseAuth != null) firebaseAuth.signOut();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String displayNameFromDb = snapshot.child("displayName").getValue(String.class);
|
|
||||||
String establishmentName = snapshot.child("establishmentName").getValue(String.class);
|
|
||||||
String ownerName = snapshot.child("ownerName").getValue(String.class);
|
|
||||||
String roleFromDb = snapshot.child("role").getValue(String.class);
|
|
||||||
|
|
||||||
String finalDisplayName = !TextUtils.isEmpty(establishmentName)
|
|
||||||
? establishmentName
|
|
||||||
: !TextUtils.isEmpty(displayNameFromDb) ? displayNameFromDb
|
|
||||||
: !TextUtils.isEmpty(ownerName) ? ownerName
|
|
||||||
: fallbackName;
|
|
||||||
|
|
||||||
String finalRole = !TextUtils.isEmpty(roleFromDb) ? roleFromDb : resolvedRole;
|
|
||||||
navigateToDashboard(email, finalDisplayName, finalRole);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void navigateToDashboard(String email, String displayName, String role) {
|
|
||||||
Intent nextScreen = selectedAccountType == AccountType.CLIENTE
|
|
||||||
? new Intent(this, ClientDashboardActivity.class)
|
|
||||||
: new Intent(this, EstablishmentDashboardActivity.class);
|
|
||||||
|
|
||||||
nextScreen.putExtra(EXTRA_ACTION_MODE, selectedAccountAction.name());
|
|
||||||
nextScreen.putExtra(EXTRA_DISPLAY_NAME, displayName);
|
|
||||||
nextScreen.putExtra(EXTRA_EMAIL, email);
|
|
||||||
nextScreen.putExtra(EXTRA_ACCOUNT_TYPE, selectedAccountType.name());
|
|
||||||
nextScreen.putExtra(EXTRA_ROLE, role);
|
|
||||||
startActivity(nextScreen);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void persistAccountInFirebase(
|
|
||||||
String ownerEmail,
|
|
||||||
String ownerName,
|
|
||||||
String role,
|
|
||||||
String ownerPhone,
|
|
||||||
String establishmentName,
|
|
||||||
String establishmentEmail,
|
|
||||||
String establishmentPhone,
|
|
||||||
String uid,
|
|
||||||
Runnable onSuccess) {
|
|
||||||
if (databaseReference == null) {
|
|
||||||
Toast.makeText(this, "Firebase indisponível.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String documentId = buildDocumentId(ownerEmail);
|
|
||||||
Map<String, Object> payload = new HashMap<>();
|
|
||||||
if (!TextUtils.isEmpty(uid)) {
|
|
||||||
payload.put("uid", uid);
|
|
||||||
}
|
|
||||||
payload.put("email", ownerEmail);
|
|
||||||
payload.put("displayName", ownerName);
|
|
||||||
payload.put("role", role);
|
|
||||||
payload.put("accountType", selectedAccountType.name());
|
|
||||||
payload.put("createdAt", System.currentTimeMillis());
|
|
||||||
|
|
||||||
if (selectedAccountType == AccountType.ESTABELECIMENTO) {
|
|
||||||
payload.put("ownerName", ownerName);
|
|
||||||
payload.put("ownerEmail", ownerEmail);
|
|
||||||
payload.put("ownerPhone", ownerPhone);
|
|
||||||
payload.put("establishmentName", establishmentName);
|
|
||||||
payload.put("establishmentEmail", establishmentEmail);
|
|
||||||
payload.put("establishmentPhone", establishmentPhone);
|
|
||||||
}
|
|
||||||
|
|
||||||
String collectionPath = selectedAccountType == AccountType.ESTABELECIMENTO ? "Restaurantes" : "Clientes";
|
|
||||||
databaseReference.child(collectionPath).child(documentId).updateChildren(payload)
|
|
||||||
.addOnSuccessListener(unused -> {
|
|
||||||
Toast.makeText(this, "Conta guardada na cloud.", Toast.LENGTH_SHORT).show();
|
|
||||||
if (onSuccess != null) {
|
|
||||||
onSuccess.run();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.addOnFailureListener(
|
|
||||||
e -> Toast.makeText(this, "Não foi possível guardar na cloud.", Toast.LENGTH_SHORT).show());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isValidPassword(String password) {
|
|
||||||
if (password.length() < 6) {
|
|
||||||
Toast.makeText(this, "A palavra-passe deve ter pelo menos 6 caracteres.", Toast.LENGTH_SHORT).show();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
boolean hasLower = false;
|
|
||||||
boolean hasDigit = false;
|
|
||||||
boolean hasSpecial = false;
|
|
||||||
|
|
||||||
for (char c : password.toCharArray()) {
|
|
||||||
if (Character.isLowerCase(c))
|
|
||||||
hasLower = true;
|
|
||||||
else if (Character.isDigit(c))
|
|
||||||
hasDigit = true;
|
|
||||||
else if (!Character.isLetterOrDigit(c))
|
|
||||||
hasSpecial = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasLower || !hasDigit || !hasSpecial) {
|
|
||||||
Toast.makeText(this, "A palavra-passe deve conter minúsculas, números e símbolos.", Toast.LENGTH_LONG)
|
|
||||||
.show();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,12 @@ package com.example.pap_teste;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
import androidx.activity.EdgeToEdge;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
@@ -14,11 +15,11 @@ import androidx.core.view.WindowInsetsCompat;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.example.pap_teste.models.Reserva;
|
import com.example.pap_teste.models.Reserva;
|
||||||
import com.google.firebase.database.DataSnapshot;
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
import com.google.firebase.database.DatabaseError;
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
import com.google.firebase.database.DatabaseReference;
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
import com.google.firebase.firestore.Query;
|
||||||
import com.google.firebase.database.ValueEventListener;
|
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -28,40 +29,36 @@ public class MinhasReservasActivity extends AppCompatActivity {
|
|||||||
private RecyclerView rvMinhasReservas;
|
private RecyclerView rvMinhasReservas;
|
||||||
private ReservaAdapter adapter;
|
private ReservaAdapter adapter;
|
||||||
private final List<Reserva> reservaList = new ArrayList<>();
|
private final List<Reserva> reservaList = new ArrayList<>();
|
||||||
private DatabaseReference databaseReference;
|
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
private String clientEmail;
|
private String clientEmail;
|
||||||
private android.widget.ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
private android.view.View emptyState;
|
private View emptyState;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
EdgeToEdge.enable(this);
|
||||||
setContentView(R.layout.activity_minhas_reservas);
|
setContentView(R.layout.activity_minhas_reservas);
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.minhasReservasRoot), (v, insets) -> {
|
|
||||||
|
View root = findViewById(R.id.minhasReservasRoot);
|
||||||
|
if (root != null) {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
return insets;
|
return insets;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
clientEmail = getIntent().getStringExtra(MainActivity.EXTRA_EMAIL);
|
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
if (clientEmail == null) {
|
|
||||||
com.google.firebase.auth.FirebaseUser user = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser();
|
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
clientEmail = user.getEmail();
|
clientEmail = user.getEmail();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback for testing if still null
|
|
||||||
if (clientEmail == null) {
|
|
||||||
clientEmail = "cliente@teste.com";
|
|
||||||
}
|
|
||||||
|
|
||||||
rvMinhasReservas = findViewById(R.id.rvMinhasReservas);
|
rvMinhasReservas = findViewById(R.id.rvMinhasReservas);
|
||||||
progressBar = findViewById(R.id.progressBar);
|
progressBar = findViewById(R.id.progressBar);
|
||||||
emptyState = findViewById(R.id.emptyState);
|
emptyState = findViewById(R.id.emptyState);
|
||||||
Button btnVoltar = findViewById(R.id.btnVoltar);
|
|
||||||
btnVoltar.setOnClickListener(v -> finish());
|
findViewById(R.id.btnVoltar).setOnClickListener(v -> finish());
|
||||||
|
|
||||||
setupAdapter();
|
setupAdapter();
|
||||||
loadReservations();
|
loadReservations();
|
||||||
@@ -85,48 +82,33 @@ public class MinhasReservasActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadReservations() {
|
private void loadReservations() {
|
||||||
databaseReference = FirebaseDatabase.getInstance().getReference("reservas");
|
if (clientEmail == null) return;
|
||||||
if (progressBar != null) progressBar.setVisibility(android.view.View.VISIBLE);
|
|
||||||
if (emptyState != null) emptyState.setVisibility(android.view.View.GONE);
|
if (progressBar != null) progressBar.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
db.collection("Reservas").whereEqualTo("clienteEmail", clientEmail)
|
||||||
|
.orderBy("createdAt", Query.Direction.DESCENDING)
|
||||||
|
.addSnapshotListener((value, error) -> {
|
||||||
|
if (progressBar != null) progressBar.setVisibility(View.GONE);
|
||||||
|
if (value == null) return;
|
||||||
|
|
||||||
databaseReference.orderByChild("clienteEmail").equalTo(clientEmail)
|
|
||||||
.addValueEventListener(new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
|
||||||
if (progressBar != null) progressBar.setVisibility(android.view.View.GONE);
|
|
||||||
reservaList.clear();
|
reservaList.clear();
|
||||||
for (DataSnapshot dataSnapshot : snapshot.getChildren()) {
|
for (QueryDocumentSnapshot doc : value) {
|
||||||
Reserva reserva = dataSnapshot.getValue(Reserva.class);
|
Reserva r = doc.toObject(Reserva.class);
|
||||||
if (reserva != null) {
|
r.setId(doc.getId());
|
||||||
reservaList.add(reserva);
|
reservaList.add(r);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Order reservations (newest first based on ID or we can just reverse the list)
|
|
||||||
java.util.Collections.reverse(reservaList);
|
|
||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
|
|
||||||
if (emptyState != null) {
|
if (emptyState != null) {
|
||||||
emptyState.setVisibility(reservaList.isEmpty() ? android.view.View.VISIBLE : android.view.View.GONE);
|
emptyState.setVisibility(reservaList.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@NonNull DatabaseError error) {
|
|
||||||
if (progressBar != null) progressBar.setVisibility(android.view.View.GONE);
|
|
||||||
Toast.makeText(MinhasReservasActivity.this, "Erro ao carregar reservas.", Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelReservation(Reserva reserva) {
|
private void cancelReservation(Reserva reserva) {
|
||||||
databaseReference.child(reserva.getId()).child("estado").setValue("Cancelada")
|
if (reserva.getId() == null) return;
|
||||||
.addOnCompleteListener(task -> {
|
db.collection("Reservas").document(reserva.getId()).update("estado", "Cancelada")
|
||||||
if (task.isSuccessful()) {
|
.addOnSuccessListener(aVoid -> Toast.makeText(this, "Reserva cancelada.", Toast.LENGTH_SHORT).show());
|
||||||
Toast.makeText(this, "Reserva cancelada.", Toast.LENGTH_SHORT).show();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "Erro ao cancelar reserva.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
import com.example.pap_teste.models.Reserva;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
import com.google.firebase.firestore.Query;
|
||||||
|
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MyReservationsFragment extends Fragment {
|
||||||
|
|
||||||
|
private RecyclerView rvReservations;
|
||||||
|
private ReservaAdapter adapter;
|
||||||
|
private List<Reserva> reservationList = new ArrayList<>();
|
||||||
|
private ProgressBar progressBar;
|
||||||
|
private View emptyState;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_my_reservations, container, false);
|
||||||
|
|
||||||
|
rvReservations = view.findViewById(R.id.rvReservations);
|
||||||
|
progressBar = view.findViewById(R.id.progressBar);
|
||||||
|
emptyState = view.findViewById(R.id.emptyState);
|
||||||
|
|
||||||
|
setupAdapter();
|
||||||
|
loadReservationsFromFirestore();
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupAdapter() {
|
||||||
|
adapter = new ReservaAdapter(reservationList, new ReservaAdapter.OnReservaActionListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckIn(Reserva reserva) {
|
||||||
|
Toast.makeText(getContext(), "Funcionalidade de Check-in em breve", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCancel(Reserva reserva) {
|
||||||
|
cancelReservation(reserva);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
rvReservations.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
|
rvReservations.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelReservation(Reserva reserva) {
|
||||||
|
if (reserva.getId() == null) return;
|
||||||
|
FirestoreManager.getInstance().getReservationsCollection().document(reserva.getId())
|
||||||
|
.update("estado", "Cancelada")
|
||||||
|
.addOnSuccessListener(aVoid -> Toast.makeText(getContext(), "Reserva cancelada.", Toast.LENGTH_SHORT).show());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadReservationsFromFirestore() {
|
||||||
|
String email = FirebaseAuth.getInstance().getCurrentUser() != null
|
||||||
|
? FirebaseAuth.getInstance().getCurrentUser().getEmail() : null;
|
||||||
|
|
||||||
|
if (email == null) {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
emptyState.setVisibility(View.VISIBLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
FirestoreManager.getInstance().getReservationsCollection()
|
||||||
|
.whereEqualTo("clienteEmail", email)
|
||||||
|
.orderBy("createdAt", Query.Direction.DESCENDING)
|
||||||
|
.addSnapshotListener((value, error) -> {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
if (value == null) return;
|
||||||
|
|
||||||
|
reservationList.clear();
|
||||||
|
for (QueryDocumentSnapshot doc : value) {
|
||||||
|
Reserva r = doc.toObject(Reserva.class);
|
||||||
|
r.setId(doc.getId());
|
||||||
|
reservationList.add(r);
|
||||||
|
}
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
emptyState.setVisibility(reservationList.isEmpty() ? View.VISIBLE : View.GONE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,336 +1,249 @@
|
|||||||
package com.example.pap_teste;
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.app.DatePickerDialog;
|
||||||
|
import android.app.TimePickerDialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
import androidx.activity.EdgeToEdge;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.core.view.WindowInsetsCompat;
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import android.widget.Button;
|
import com.example.pap_teste.models.FoodCategory;
|
||||||
|
import com.example.pap_teste.models.Mesa;
|
||||||
|
import com.example.pap_teste.models.Reserva;
|
||||||
|
import com.example.pap_teste.models.Restaurant;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
import com.google.firebase.firestore.QueryDocumentSnapshot;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public class NovaReservaActivity extends AppCompatActivity {
|
public class NovaReservaActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private enum State {
|
private enum State { CATEGORIES, RESTAURANTS, DETAILS }
|
||||||
CATEGORIES, RESTAURANTS, DETAILS
|
|
||||||
}
|
|
||||||
|
|
||||||
private State currentState = State.CATEGORIES;
|
private State currentState = State.CATEGORIES;
|
||||||
private String selectedCategory = null;
|
private String selectedCategory = null;
|
||||||
private com.example.pap_teste.models.Restaurant selectedRestaurant = null;
|
private Restaurant selectedRestaurant = null;
|
||||||
|
private Mesa selectedMesa = null;
|
||||||
|
private String selectedDate = null;
|
||||||
|
private String selectedTime = null;
|
||||||
|
|
||||||
private androidx.recyclerview.widget.RecyclerView rvCategories, rvRestaurants;
|
private RecyclerView rvCategories, rvRestaurants, rvTables;
|
||||||
private android.view.View scrollNovaReserva;
|
private View scrollNovaReserva;
|
||||||
private android.widget.TextView txtTitle;
|
private TextView txtTitle;
|
||||||
private android.widget.ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
|
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
EdgeToEdge.enable(this);
|
||||||
setContentView(R.layout.activity_nova_reserva);
|
setContentView(R.layout.activity_nova_reserva);
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.novaReservaRoot), (v, insets) -> {
|
|
||||||
|
View root = findViewById(R.id.novaReservaRoot);
|
||||||
|
if (root != null) {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
return insets;
|
return insets;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initViews();
|
||||||
|
setupCategories();
|
||||||
|
updateViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initViews() {
|
||||||
rvCategories = findViewById(R.id.rvCategories);
|
rvCategories = findViewById(R.id.rvCategories);
|
||||||
rvRestaurants = findViewById(R.id.rvRestaurants);
|
rvRestaurants = findViewById(R.id.rvRestaurants);
|
||||||
|
rvTables = findViewById(R.id.rvTables);
|
||||||
scrollNovaReserva = findViewById(R.id.scrollNovaReserva);
|
scrollNovaReserva = findViewById(R.id.scrollNovaReserva);
|
||||||
txtTitle = findViewById(R.id.txtTituloNovaReserva);
|
txtTitle = findViewById(R.id.txtTituloNovaReserva);
|
||||||
progressBar = findViewById(R.id.progressBar);
|
progressBar = findViewById(R.id.progressBar);
|
||||||
|
|
||||||
Button back = findViewById(R.id.btnVoltar);
|
findViewById(R.id.btnVoltar).setOnClickListener(v -> handleBackNavigation());
|
||||||
if (back != null) {
|
findViewById(R.id.btnSelectDate).setOnClickListener(v -> showDatePicker());
|
||||||
back.setOnClickListener(v -> handleBackNavigation());
|
findViewById(R.id.btnSelectTime).setOnClickListener(v -> showTimePicker());
|
||||||
}
|
findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> validateAndSave());
|
||||||
|
|
||||||
setupCategories();
|
|
||||||
updateViewState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleBackNavigation() {
|
private void handleBackNavigation() {
|
||||||
if (currentState == State.RESTAURANTS) {
|
if (currentState == State.RESTAURANTS) {
|
||||||
currentState = State.CATEGORIES;
|
currentState = State.CATEGORIES;
|
||||||
updateViewState();
|
|
||||||
} else if (currentState == State.DETAILS) {
|
} else if (currentState == State.DETAILS) {
|
||||||
currentState = State.RESTAURANTS;
|
currentState = State.RESTAURANTS;
|
||||||
updateViewState();
|
|
||||||
} else {
|
} else {
|
||||||
finish();
|
finish();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
updateViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateViewState() {
|
private void updateViewState() {
|
||||||
rvCategories
|
rvCategories.setVisibility(currentState == State.CATEGORIES ? View.VISIBLE : View.GONE);
|
||||||
.setVisibility(currentState == State.CATEGORIES ? android.view.View.VISIBLE : android.view.View.GONE);
|
rvRestaurants.setVisibility(currentState == State.RESTAURANTS ? View.VISIBLE : View.GONE);
|
||||||
rvRestaurants
|
scrollNovaReserva.setVisibility(currentState == State.DETAILS ? View.VISIBLE : View.GONE);
|
||||||
.setVisibility(currentState == State.RESTAURANTS ? android.view.View.VISIBLE : android.view.View.GONE);
|
|
||||||
scrollNovaReserva
|
|
||||||
.setVisibility(currentState == State.DETAILS ? android.view.View.VISIBLE : android.view.View.GONE);
|
|
||||||
|
|
||||||
if (currentState == State.CATEGORIES) {
|
switch (currentState) {
|
||||||
txtTitle.setText("Escolha o tema");
|
case CATEGORIES: txtTitle.setText("Escolha o tema"); break;
|
||||||
} else if (currentState == State.RESTAURANTS) {
|
case RESTAURANTS: txtTitle.setText("Restaurantes: " + selectedCategory); break;
|
||||||
txtTitle.setText("Restaurantes: " + selectedCategory);
|
case DETAILS: txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : "")); break;
|
||||||
} else {
|
|
||||||
txtTitle.setText("Reserva: " + (selectedRestaurant != null ? selectedRestaurant.getName() : ""));
|
|
||||||
setupReservationOptions();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupCategories() {
|
private void setupCategories() {
|
||||||
java.util.List<com.example.pap_teste.models.FoodCategory> cats = new java.util.ArrayList<>();
|
db.collection("Categorias").get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||||
cats.add(new com.example.pap_teste.models.FoodCategory("Carnes", R.drawable.cat_carnes));
|
List<FoodCategory> cats = new ArrayList<>();
|
||||||
cats.add(new com.example.pap_teste.models.FoodCategory("Massas", R.drawable.cat_massas));
|
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||||
cats.add(new com.example.pap_teste.models.FoodCategory("Sushi", R.drawable.cat_sushi));
|
FoodCategory cat = doc.toObject(FoodCategory.class);
|
||||||
cats.add(new com.example.pap_teste.models.FoodCategory("Pizzas", R.drawable.cat_pizzas));
|
cats.add(cat);
|
||||||
cats.add(new com.example.pap_teste.models.FoodCategory("Sobremesas", R.drawable.cat_sobremesas));
|
}
|
||||||
|
|
||||||
rvCategories.setLayoutManager(new androidx.recyclerview.widget.LinearLayoutManager(this));
|
rvCategories.setLayoutManager(new LinearLayoutManager(this));
|
||||||
rvCategories.setAdapter(new FoodCategoryAdapter(cats, category -> {
|
rvCategories.setAdapter(new FoodCategoryAdapter(cats, category -> {
|
||||||
selectedCategory = category.getName();
|
selectedCategory = category.getName();
|
||||||
currentState = State.RESTAURANTS;
|
currentState = State.RESTAURANTS;
|
||||||
setupRestaurants();
|
loadRestaurants();
|
||||||
updateViewState();
|
updateViewState();
|
||||||
}));
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupRestaurants() {
|
private void loadRestaurants() {
|
||||||
java.util.List<com.example.pap_teste.models.Restaurant> filteredList = new java.util.ArrayList<>();
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
com.google.firebase.database.DatabaseReference usersRef = com.google.firebase.database.FirebaseDatabase
|
db.collection("Restaurantes").whereEqualTo("category", selectedCategory).get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||||
.getInstance().getReference("Restaurantes");
|
progressBar.setVisibility(View.GONE);
|
||||||
|
List<Restaurant> list = new ArrayList<>();
|
||||||
if (progressBar != null)
|
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||||
progressBar.setVisibility(android.view.View.VISIBLE);
|
Restaurant r = doc.toObject(Restaurant.class);
|
||||||
|
r.setId(doc.getId());
|
||||||
usersRef.orderByChild("category").equalTo(selectedCategory)
|
list.add(r);
|
||||||
.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(
|
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
if (progressBar != null)
|
|
||||||
progressBar.setVisibility(android.view.View.GONE);
|
|
||||||
filteredList.clear();
|
|
||||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
|
||||||
String role = ds.child("role").getValue(String.class);
|
|
||||||
String accountType = ds.child("accountType").getValue(String.class);
|
|
||||||
|
|
||||||
// Aceitar todos os registos na coleção Restaurantes
|
|
||||||
if (true) {
|
|
||||||
String name = ds.child("establishmentName").getValue(String.class);
|
|
||||||
if (name == null)
|
|
||||||
name = ds.child("displayName").getValue(String.class);
|
|
||||||
String email = ds.child("email").getValue(String.class);
|
|
||||||
String cat = ds.child("category").getValue(String.class);
|
|
||||||
String logoUrl = ds.child("logoUrl").getValue(String.class);
|
|
||||||
|
|
||||||
if (name != null && email != null) {
|
|
||||||
filteredList.add(new com.example.pap_teste.models.Restaurant(name, cat, email,
|
|
||||||
false, logoUrl));
|
|
||||||
}
|
}
|
||||||
}
|
rvRestaurants.setLayoutManager(new LinearLayoutManager(this));
|
||||||
}
|
rvRestaurants.setAdapter(new RestaurantAdapter(list, restaurant -> {
|
||||||
|
|
||||||
rvRestaurants.setLayoutManager(
|
|
||||||
new androidx.recyclerview.widget.LinearLayoutManager(NovaReservaActivity.this));
|
|
||||||
rvRestaurants.setAdapter(new RestaurantAdapter(filteredList, restaurant -> {
|
|
||||||
selectedRestaurant = restaurant;
|
selectedRestaurant = restaurant;
|
||||||
currentState = State.DETAILS;
|
currentState = State.DETAILS;
|
||||||
|
loadTables();
|
||||||
updateViewState();
|
updateViewState();
|
||||||
}));
|
}));
|
||||||
}
|
}).addOnFailureListener(e -> {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
@Override
|
Toast.makeText(this, "Erro ao carregar restaurantes.", Toast.LENGTH_SHORT).show();
|
||||||
public void onCancelled(
|
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
if (progressBar != null)
|
|
||||||
progressBar.setVisibility(android.view.View.GONE);
|
|
||||||
android.widget.Toast.makeText(NovaReservaActivity.this, "Erro ao carregar restaurantes.",
|
|
||||||
android.widget.Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private String selectedDate = null;
|
private void loadTables() {
|
||||||
private String selectedTime = null;
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
|
db.collection("Mesas").whereEqualTo("restauranteEmail", selectedRestaurant.getEmail()).get().addOnSuccessListener(queryDocumentSnapshots -> {
|
||||||
private void setupReservationOptions() {
|
progressBar.setVisibility(View.GONE);
|
||||||
android.widget.Button btnDate = findViewById(R.id.btnSelectDate);
|
List<Mesa> tables = new ArrayList<>();
|
||||||
android.widget.Button btnTime = findViewById(R.id.btnSelectTime);
|
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||||
|
Mesa m = doc.toObject(Mesa.class);
|
||||||
btnDate.setOnClickListener(v -> {
|
m.setId(doc.getId());
|
||||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
tables.add(m);
|
||||||
new android.app.DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
}
|
||||||
selectedDate = dayOfMonth + "/" + (month + 1) + "/" + year;
|
rvTables.setLayoutManager(new GridLayoutManager(this, 3));
|
||||||
btnDate.setText(selectedDate);
|
rvTables.setAdapter(new TableSelectionAdapter(tables, mesa -> selectedMesa = mesa));
|
||||||
}, cal.get(java.util.Calendar.YEAR), cal.get(java.util.Calendar.MONTH),
|
|
||||||
cal.get(java.util.Calendar.DAY_OF_MONTH)).show();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
btnTime.setOnClickListener(v -> {
|
|
||||||
java.util.Calendar cal = java.util.Calendar.getInstance();
|
|
||||||
new android.app.TimePickerDialog(this, (view, hourOfDay, minute) -> {
|
|
||||||
selectedTime = String.format(java.util.Locale.getDefault(), "%02d:%02d", hourOfDay, minute);
|
|
||||||
btnTime.setText(selectedTime);
|
|
||||||
}, cal.get(java.util.Calendar.HOUR_OF_DAY), cal.get(java.util.Calendar.MINUTE), true).show();
|
|
||||||
});
|
|
||||||
|
|
||||||
findViewById(R.id.btnConfirmarReserva).setOnClickListener(v -> saveReservation());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveReservation() {
|
private void showDatePicker() {
|
||||||
android.widget.EditText etPartySize = findViewById(R.id.etPartySize);
|
Calendar cal = Calendar.getInstance();
|
||||||
int val = 0;
|
new DatePickerDialog(this, (view, year, month, day) -> {
|
||||||
try {
|
selectedDate = String.format(Locale.getDefault(), "%02d/%02d/%d", day, month + 1, year);
|
||||||
val = Integer.parseInt(etPartySize.getText().toString());
|
((Button)findViewById(R.id.btnSelectDate)).setText(selectedDate);
|
||||||
} catch (Exception e) {
|
}, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)).show();
|
||||||
}
|
}
|
||||||
final int partySize = val;
|
|
||||||
|
|
||||||
if (selectedDate == null || selectedTime == null || partySize == 0) {
|
private void showTimePicker() {
|
||||||
android.widget.Toast.makeText(this, "Por favor, selecione data, hora e número de pessoas.",
|
Calendar cal = Calendar.getInstance();
|
||||||
android.widget.Toast.LENGTH_SHORT).show();
|
new TimePickerDialog(this, (view, hour, min) -> {
|
||||||
|
selectedTime = String.format(Locale.getDefault(), "%02d:%02d", hour, min);
|
||||||
|
((Button)findViewById(R.id.btnSelectTime)).setText(selectedTime);
|
||||||
|
}, cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE), true).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateAndSave() {
|
||||||
|
EditText etPartySize = findViewById(R.id.etPartySize);
|
||||||
|
String partySizeStr = etPartySize.getText().toString();
|
||||||
|
|
||||||
|
if (selectedDate == null || selectedTime == null || selectedMesa == null || partySizeStr.isEmpty()) {
|
||||||
|
Toast.makeText(this, "Preencha todos os campos.", Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String restEmail = selectedRestaurant.getEmail();
|
int partySize = Integer.parseInt(partySizeStr);
|
||||||
|
if (partySize > selectedMesa.getCapacidade()) {
|
||||||
if (progressBar != null)
|
Toast.makeText(this, "A mesa só suporta " + selectedMesa.getCapacidade() + " pessoas.", Toast.LENGTH_SHORT).show();
|
||||||
progressBar.setVisibility(android.view.View.VISIBLE);
|
|
||||||
android.widget.Button btnConfirmar = findViewById(R.id.btnConfirmarReserva);
|
|
||||||
if (btnConfirmar != null)
|
|
||||||
btnConfirmar.setEnabled(false);
|
|
||||||
|
|
||||||
com.google.firebase.database.DatabaseReference mesasRef = com.google.firebase.database.FirebaseDatabase
|
|
||||||
.getInstance().getReference("Mesas");
|
|
||||||
|
|
||||||
mesasRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
int totalMesas = 0;
|
|
||||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
|
||||||
com.example.pap_teste.models.Mesa m = ds.getValue(com.example.pap_teste.models.Mesa.class);
|
|
||||||
if (m != null && m.getRestauranteEmail() != null && restEmail.trim().equalsIgnoreCase(m.getRestauranteEmail().trim())) {
|
|
||||||
totalMesas++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (totalMesas == 0) {
|
|
||||||
proceedWithReservation(partySize);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
checkReservationsAndSave(totalMesas, partySize);
|
checkConflictAndSave(partySize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void checkConflictAndSave(int guests) {
|
||||||
public void onCancelled(@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
progressBar.setVisibility(View.VISIBLE);
|
||||||
proceedWithReservation(partySize);
|
db.collection("Reservas")
|
||||||
|
.whereEqualTo("restauranteEmail", selectedRestaurant.getEmail())
|
||||||
|
.whereEqualTo("data", selectedDate)
|
||||||
|
.whereEqualTo("hora", selectedTime)
|
||||||
|
.get()
|
||||||
|
.addOnSuccessListener(queryDocumentSnapshots -> {
|
||||||
|
boolean conflict = false;
|
||||||
|
for (QueryDocumentSnapshot doc : queryDocumentSnapshots) {
|
||||||
|
Long tableNum = doc.getLong("tableNumber");
|
||||||
|
if (tableNum != null && tableNum == selectedMesa.getNumero()) {
|
||||||
|
conflict = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conflict) {
|
||||||
|
progressBar.setVisibility(View.GONE);
|
||||||
|
Toast.makeText(this, "Esta mesa já está reservada.", Toast.LENGTH_LONG).show();
|
||||||
|
} else {
|
||||||
|
saveToFirestore(guests);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkReservationsAndSave(int totalMesas, final int partySize) {
|
private void saveToFirestore(int guests) {
|
||||||
String restEmail = selectedRestaurant.getEmail();
|
String userEmail = FirebaseAuth.getInstance().getCurrentUser() != null ? FirebaseAuth.getInstance().getCurrentUser().getEmail() : "anon@test.com";
|
||||||
com.google.firebase.database.DatabaseReference reservasRef = com.google.firebase.database.FirebaseDatabase
|
Reserva reserva = new Reserva(
|
||||||
.getInstance().getReference("reservas");
|
null,
|
||||||
|
|
||||||
reservasRef.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(
|
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DataSnapshot snapshot) {
|
|
||||||
int ocupadas = 0;
|
|
||||||
java.util.Map<String, Integer> ocupacaoPorHora = new java.util.HashMap<>();
|
|
||||||
|
|
||||||
for (com.google.firebase.database.DataSnapshot ds : snapshot.getChildren()) {
|
|
||||||
com.example.pap_teste.models.Reserva r = ds
|
|
||||||
.getValue(com.example.pap_teste.models.Reserva.class);
|
|
||||||
if (r != null && r.getRestauranteEmail() != null &&
|
|
||||||
r.getRestauranteEmail().trim().equalsIgnoreCase(restEmail.trim()) &&
|
|
||||||
selectedDate.equals(r.getData()) && !"Cancelada".equals(r.getEstado())
|
|
||||||
&& !"Recusada".equals(r.getEstado())) {
|
|
||||||
int count = ocupacaoPorHora.getOrDefault(r.getHora(), 0) + 1;
|
|
||||||
ocupacaoPorHora.put(r.getHora(), count);
|
|
||||||
if (selectedTime.equals(r.getHora())) {
|
|
||||||
ocupadas++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ocupadas >= totalMesas) {
|
|
||||||
String sugestao = "";
|
|
||||||
String[] horasComuns = { "12:00", "12:30", "13:00", "13:30", "14:00", "19:00", "19:30",
|
|
||||||
"20:00", "20:30", "21:00", "21:30", "22:00" };
|
|
||||||
for (String h : horasComuns) {
|
|
||||||
if (ocupacaoPorHora.getOrDefault(h, 0) < totalMesas && !h.equals(selectedTime)) {
|
|
||||||
sugestao = h;
|
|
||||||
break; // Encontramos a primeira sugestão livre
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String msg = "Não há mesas disponíveis para as " + selectedTime + ".";
|
|
||||||
if (!sugestao.isEmpty()) {
|
|
||||||
msg += " Sugestão: tente reservar para as " + sugestao + ".";
|
|
||||||
} else {
|
|
||||||
msg += " Tente para outro dia.";
|
|
||||||
}
|
|
||||||
android.widget.Toast
|
|
||||||
.makeText(NovaReservaActivity.this, msg, android.widget.Toast.LENGTH_LONG).show();
|
|
||||||
} else {
|
|
||||||
proceedWithReservation(partySize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(
|
|
||||||
@androidx.annotation.NonNull com.google.firebase.database.DatabaseError error) {
|
|
||||||
proceedWithReservation(partySize);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void proceedWithReservation(int partySize) {
|
|
||||||
String userEmail = com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser() != null
|
|
||||||
? com.google.firebase.auth.FirebaseAuth.getInstance().getCurrentUser().getEmail()
|
|
||||||
: "cliente@teste.com";
|
|
||||||
com.google.firebase.database.DatabaseReference ref = com.google.firebase.database.FirebaseDatabase.getInstance()
|
|
||||||
.getReference("reservas");
|
|
||||||
String id = ref.push().getKey();
|
|
||||||
|
|
||||||
com.example.pap_teste.models.Reserva reserva = new com.example.pap_teste.models.Reserva(
|
|
||||||
id,
|
|
||||||
userEmail,
|
userEmail,
|
||||||
selectedRestaurant.getName(),
|
selectedRestaurant.getName(),
|
||||||
selectedRestaurant.getEmail(),
|
selectedRestaurant.getEmail(),
|
||||||
selectedDate,
|
selectedDate,
|
||||||
selectedTime,
|
selectedTime,
|
||||||
partySize,
|
guests,
|
||||||
"Pendente");
|
"Pendente"
|
||||||
|
);
|
||||||
|
reserva.setTableNumber(selectedMesa.getNumero());
|
||||||
|
|
||||||
if (id != null) {
|
db.collection("Reservas").add(reserva).addOnSuccessListener(documentReference -> {
|
||||||
ref.child(id).setValue(reserva).addOnCompleteListener(task -> {
|
progressBar.setVisibility(View.GONE);
|
||||||
if (progressBar != null)
|
Toast.makeText(this, "Reserva realizada com sucesso!", Toast.LENGTH_LONG).show();
|
||||||
progressBar.setVisibility(android.view.View.GONE);
|
|
||||||
android.widget.Button btnConfirmar = findViewById(R.id.btnConfirmarReserva);
|
|
||||||
if (btnConfirmar != null)
|
|
||||||
btnConfirmar.setEnabled(true);
|
|
||||||
|
|
||||||
if (task.isSuccessful()) {
|
|
||||||
android.widget.Toast
|
|
||||||
.makeText(NovaReservaActivity.this, "Reserva solicitada com sucesso!",
|
|
||||||
android.widget.Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
finish();
|
finish();
|
||||||
} else {
|
}).addOnFailureListener(e -> {
|
||||||
android.widget.Toast
|
progressBar.setVisibility(View.GONE);
|
||||||
.makeText(NovaReservaActivity.this, "Erro ao salvar reserva.",
|
Toast.makeText(this, "Erro ao guardar reserva.", Toast.LENGTH_SHORT).show();
|
||||||
android.widget.Toast.LENGTH_SHORT)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.animation.AlphaAnimation;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
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.android.material.button.MaterialButton;
|
||||||
|
|
||||||
|
public class OnboardingActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
EdgeToEdge.enable(this);
|
||||||
|
setContentView(R.layout.activity_onboarding);
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(findViewById(android.R.id.content), (v, insets) -> {
|
||||||
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
|
||||||
|
ImageView logo = findViewById(R.id.logo);
|
||||||
|
TextView tagline = findViewById(R.id.tagline);
|
||||||
|
MaterialButton btnGetStarted = findViewById(R.id.btnGetStarted);
|
||||||
|
|
||||||
|
// Simple animation
|
||||||
|
Animation fadeIn = new AlphaAnimation(0, 1);
|
||||||
|
fadeIn.setDuration(1500);
|
||||||
|
logo.startAnimation(fadeIn);
|
||||||
|
tagline.startAnimation(fadeIn);
|
||||||
|
|
||||||
|
btnGetStarted.setOnClickListener(v -> {
|
||||||
|
android.app.ActivityOptions options = android.app.ActivityOptions.makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out);
|
||||||
|
startActivity(new Intent(OnboardingActivity.this, MainActivity.class), options.toBundle());
|
||||||
|
finish();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,71 +3,60 @@ package com.example.pap_teste;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageView;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import androidx.activity.EdgeToEdge;
|
import androidx.activity.EdgeToEdge;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.core.graphics.Insets;
|
import androidx.core.graphics.Insets;
|
||||||
import androidx.core.view.ViewCompat;
|
import androidx.core.view.ViewCompat;
|
||||||
import androidx.core.view.WindowInsetsCompat;
|
import androidx.core.view.WindowInsetsCompat;
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
import com.google.firebase.storage.FirebaseStorage;
|
import com.google.firebase.storage.FirebaseStorage;
|
||||||
import com.google.firebase.storage.StorageReference;
|
import com.google.firebase.storage.StorageReference;
|
||||||
import java.util.UUID;
|
|
||||||
import com.google.firebase.database.DatabaseReference;
|
|
||||||
import com.google.firebase.database.FirebaseDatabase;
|
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class ProfileDashboardActivity extends AppCompatActivity {
|
public class ProfileDashboardActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private EditText inputName, inputPhone, inputEmailEdit;
|
private EditText inputName, inputPhone, inputEmailEdit;
|
||||||
private String email, documentId, photoUrl;
|
private String photoUrl;
|
||||||
private DatabaseReference databaseReference;
|
|
||||||
private ImageView imgProfile;
|
private ImageView imgProfile;
|
||||||
private androidx.activity.result.ActivityResultLauncher<Intent> imagePickerLauncher;
|
private androidx.activity.result.ActivityResultLauncher<Intent> imagePickerLauncher;
|
||||||
|
private final FirebaseFirestore db = FirebaseFirestore.getInstance();
|
||||||
|
private final FirebaseAuth mAuth = FirebaseAuth.getInstance();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
EdgeToEdge.enable(this);
|
EdgeToEdge.enable(this);
|
||||||
setContentView(R.layout.activity_profile_dashboard);
|
setContentView(R.layout.activity_profile_dashboard);
|
||||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.profileRoot), (v, insets) -> {
|
|
||||||
|
View root = findViewById(R.id.profileRoot);
|
||||||
|
if (root != null) {
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(root, (v, insets) -> {
|
||||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||||
return insets;
|
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("Clientes");
|
|
||||||
|
|
||||||
inputName = findViewById(R.id.inputProfileName);
|
inputName = findViewById(R.id.inputProfileName);
|
||||||
inputPhone = findViewById(R.id.inputProfilePhone);
|
inputPhone = findViewById(R.id.inputProfilePhone);
|
||||||
inputEmailEdit = findViewById(R.id.inputProfileEmail);
|
inputEmailEdit = findViewById(R.id.inputProfileEmail);
|
||||||
imgProfile = findViewById(R.id.imgProfile);
|
imgProfile = findViewById(R.id.imgProfile);
|
||||||
|
|
||||||
if (currentName != null) {
|
fetchProfileData();
|
||||||
inputName.setText(currentName);
|
|
||||||
}
|
|
||||||
if (email != null) {
|
|
||||||
inputEmailEdit.setText(email);
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchAdditionalProfileData();
|
|
||||||
|
|
||||||
imagePickerLauncher = registerForActivityResult(
|
imagePickerLauncher = registerForActivityResult(
|
||||||
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
new androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult(),
|
||||||
@@ -82,7 +71,7 @@ public class ProfileDashboardActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
findViewById(R.id.cardProfileBig).setOnClickListener(v -> {
|
findViewById(R.id.cardProfileBig).setOnClickListener(v -> {
|
||||||
String[] options = {"Galeria", "URL da Imagem"};
|
String[] options = {"Galeria", "URL da Imagem"};
|
||||||
androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
builder.setTitle("Escolher Foto de Perfil");
|
builder.setTitle("Escolher Foto de Perfil");
|
||||||
builder.setItems(options, (dialog, which) -> {
|
builder.setItems(options, (dialog, which) -> {
|
||||||
if (which == 0) {
|
if (which == 0) {
|
||||||
@@ -96,79 +85,48 @@ public class ProfileDashboardActivity extends AppCompatActivity {
|
|||||||
builder.show();
|
builder.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
Button btnSave = findViewById(R.id.btnSaveProfile);
|
findViewById(R.id.btnVoltar).setOnClickListener(v -> finish());
|
||||||
Button btnBack = findViewById(R.id.btnVoltar);
|
findViewById(R.id.btnSaveProfile).setOnClickListener(v -> saveProfile());
|
||||||
Button btnFavs = findViewById(R.id.btnFavoritos);
|
findViewById(R.id.btnLogOut).setOnClickListener(v -> performLogOut());
|
||||||
Button btnRes = findViewById(R.id.btnMinhasReservas);
|
findViewById(R.id.btnFavoritos).setOnClickListener(v -> startActivity(new Intent(this, FavoritosActivity.class)));
|
||||||
Button btnLogOut = findViewById(R.id.btnLogOut);
|
findViewById(R.id.btnMinhasReservas).setOnClickListener(v -> startActivity(new Intent(this, MinhasReservasActivity.class)));
|
||||||
|
|
||||||
btnBack.setOnClickListener(v -> finish());
|
|
||||||
btnSave.setOnClickListener(v -> saveProfile());
|
|
||||||
|
|
||||||
btnLogOut.setOnClickListener(v -> performLogOut());
|
|
||||||
|
|
||||||
btnFavs.setOnClickListener(v -> {
|
|
||||||
startActivity(new Intent(this, FavoritosActivity.class));
|
|
||||||
});
|
|
||||||
|
|
||||||
btnRes.setOnClickListener(v -> {
|
|
||||||
startActivity(new Intent(this, MinhasReservasActivity.class));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void performLogOut() {
|
private void performLogOut() {
|
||||||
try {
|
mAuth.signOut();
|
||||||
FirebaseAuth.getInstance().signOut();
|
Toast.makeText(this, "Sessão terminada.", Toast.LENGTH_SHORT).show();
|
||||||
Toast.makeText(this, "Sessão terminada com sucesso.", Toast.LENGTH_SHORT).show();
|
|
||||||
|
|
||||||
Intent intent = new Intent(this, MainActivity.class);
|
Intent intent = new Intent(this, MainActivity.class);
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
finish();
|
finish();
|
||||||
} catch (Exception e) {
|
|
||||||
Toast.makeText(this, "Erro ao terminar sessão: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
|
||||||
android.util.Log.e("LogOut", "Error during sign out", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showUrlInputDialog() {
|
private void showUrlInputDialog() {
|
||||||
androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(this);
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
builder.setTitle("Inserir URL da Imagem");
|
builder.setTitle("Inserir URL da Imagem");
|
||||||
|
|
||||||
final EditText input = new EditText(this);
|
final EditText input = new EditText(this);
|
||||||
input.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_URI);
|
input.setInputType(android.text.InputType.TYPE_TEXT_VARIATION_URI);
|
||||||
input.setHint("https://exemplo.com/foto.jpg");
|
|
||||||
builder.setView(input);
|
builder.setView(input);
|
||||||
|
|
||||||
builder.setPositiveButton("Confirmar", (dialog, which) -> {
|
builder.setPositiveButton("Confirmar", (dialog, which) -> {
|
||||||
String url = input.getText().toString().trim();
|
photoUrl = input.getText().toString().trim();
|
||||||
if (!TextUtils.isEmpty(url)) {
|
if (!TextUtils.isEmpty(photoUrl)) {
|
||||||
this.photoUrl = url;
|
|
||||||
Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
||||||
Toast.makeText(this, "URL definida!", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.setNegativeButton("Cancelar", (dialog, which) -> dialog.cancel());
|
builder.setNegativeButton("Cancelar", null);
|
||||||
|
|
||||||
builder.show();
|
builder.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchAdditionalProfileData() {
|
private void fetchProfileData() {
|
||||||
if (documentId == null)
|
FirebaseUser user = mAuth.getCurrentUser();
|
||||||
return;
|
if (user == null) return;
|
||||||
databaseReference.child(documentId).get().addOnSuccessListener(snapshot -> {
|
|
||||||
|
db.collection("Clientes").document(user.getUid()).get().addOnSuccessListener(snapshot -> {
|
||||||
if (snapshot.exists()) {
|
if (snapshot.exists()) {
|
||||||
String phone = snapshot.child("ownerPhone").getValue(String.class);
|
inputName.setText(snapshot.getString("displayName"));
|
||||||
if (phone == null)
|
inputPhone.setText(snapshot.getString("phone"));
|
||||||
phone = snapshot.child("phone").getValue(String.class);
|
inputEmailEdit.setText(snapshot.getString("email"));
|
||||||
if (phone != null)
|
photoUrl = snapshot.getString("photoUrl");
|
||||||
inputPhone.setText(phone);
|
|
||||||
|
|
||||||
String dbEmail = snapshot.child("email").getValue(String.class);
|
|
||||||
if (dbEmail != null)
|
|
||||||
inputEmailEdit.setText(dbEmail);
|
|
||||||
|
|
||||||
photoUrl = snapshot.child("photoUrl").getValue(String.class);
|
|
||||||
if (photoUrl != null && !photoUrl.isEmpty()) {
|
if (photoUrl != null && !photoUrl.isEmpty()) {
|
||||||
Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
||||||
}
|
}
|
||||||
@@ -176,65 +134,36 @@ public class ProfileDashboardActivity extends AppCompatActivity {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadImageToFirebase(Uri imageUri) {
|
private void uploadImageToFirebase(android.net.Uri imageUri) {
|
||||||
if (documentId == null)
|
FirebaseUser user = mAuth.getCurrentUser();
|
||||||
return;
|
if (user == null) return;
|
||||||
|
|
||||||
StorageReference storageRef = FirebaseStorage.getInstance().getReference()
|
StorageReference storageRef = FirebaseStorage.getInstance().getReference()
|
||||||
.child("profile_pics/" + UUID.randomUUID().toString());
|
.child("profile_pics/" + user.getUid());
|
||||||
|
|
||||||
storageRef.putFile(imageUri).addOnSuccessListener(taskSnapshot -> {
|
storageRef.putFile(imageUri).addOnSuccessListener(taskSnapshot -> {
|
||||||
storageRef.getDownloadUrl().addOnSuccessListener(uri -> {
|
storageRef.getDownloadUrl().addOnSuccessListener(uri -> {
|
||||||
photoUrl = uri.toString();
|
photoUrl = uri.toString();
|
||||||
Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
Glide.with(this).load(photoUrl).circleCrop().into(imgProfile);
|
||||||
|
db.collection("Clientes").document(user.getUid()).update("photoUrl", photoUrl);
|
||||||
// Salvar a nova URL da foto imediatamente na DB
|
|
||||||
Map<String, Object> photoUpdate = new HashMap<>();
|
|
||||||
photoUpdate.put("photoUrl", photoUrl);
|
|
||||||
databaseReference.child(documentId).updateChildren(photoUpdate).addOnCompleteListener(dbTask -> {
|
|
||||||
if (dbTask.isSuccessful()) {
|
|
||||||
Toast.makeText(this, "Foto atualizada com sucesso!", Toast.LENGTH_SHORT).show();
|
|
||||||
} else {
|
|
||||||
Toast.makeText(this, "Foto enviada, mas não foi possível atualizar o perfil.", Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).addOnFailureListener(e -> {
|
|
||||||
Toast.makeText(this, "Falha no upload: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
|
||||||
android.util.Log.e("ProfileUpload", "Upload failed", e);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveProfile() {
|
private void saveProfile() {
|
||||||
if (documentId == null)
|
FirebaseUser user = mAuth.getCurrentUser();
|
||||||
return;
|
if (user == null) return;
|
||||||
|
|
||||||
String newName = inputName.getText().toString().trim();
|
|
||||||
String newPhone = inputPhone.getText().toString().trim();
|
|
||||||
String newEmail = inputEmailEdit.getText().toString().trim();
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(newName)) {
|
|
||||||
Toast.makeText(this, "Indique um nome.", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, Object> updates = new HashMap<>();
|
Map<String, Object> updates = new HashMap<>();
|
||||||
updates.put("displayName", newName);
|
updates.put("displayName", inputName.getText().toString().trim());
|
||||||
updates.put("phone", newPhone);
|
updates.put("phone", inputPhone.getText().toString().trim());
|
||||||
updates.put("email", newEmail);
|
updates.put("email", inputEmailEdit.getText().toString().trim());
|
||||||
if (photoUrl != null) {
|
if (photoUrl != null) updates.put("photoUrl", photoUrl);
|
||||||
updates.put("photoUrl", photoUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
databaseReference.child(documentId).updateChildren(updates)
|
db.collection("Clientes").document(user.getUid()).update(updates)
|
||||||
.addOnSuccessListener(aVoid -> {
|
.addOnSuccessListener(aVoid -> {
|
||||||
Toast.makeText(this, "Perfil atualizado!", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "Perfil atualizado!", Toast.LENGTH_SHORT).show();
|
||||||
Intent resultIntent = new Intent();
|
|
||||||
resultIntent.putExtra(MainActivity.EXTRA_DISPLAY_NAME, newName);
|
|
||||||
setResult(RESULT_OK, resultIntent);
|
|
||||||
finish();
|
finish();
|
||||||
})
|
});
|
||||||
.addOnFailureListener(
|
|
||||||
e -> Toast.makeText(this, "Falha ao atualizar perfil.", Toast.LENGTH_SHORT).show());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
app/src/main/java/com/example/pap_teste/ProfileFragment.java
Normal file
51
app/src/main/java/com/example/pap_teste/ProfileFragment.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
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.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.auth.FirebaseUser;
|
||||||
|
|
||||||
|
public class ProfileFragment extends Fragment {
|
||||||
|
|
||||||
|
private ImageView imgProfile;
|
||||||
|
private TextView txtName, txtEmail;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
|
View view = inflater.inflate(R.layout.fragment_profile, container, false);
|
||||||
|
|
||||||
|
imgProfile = view.findViewById(R.id.imgProfile);
|
||||||
|
txtName = view.findViewById(R.id.txtProfileName);
|
||||||
|
txtEmail = view.findViewById(R.id.txtProfileEmail);
|
||||||
|
|
||||||
|
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
||||||
|
if (user != null) {
|
||||||
|
txtName.setText(user.getDisplayName() != null ? user.getDisplayName() : "Utilizador");
|
||||||
|
txtEmail.setText(user.getEmail());
|
||||||
|
|
||||||
|
if (user.getPhotoUrl() != null) {
|
||||||
|
Glide.with(this).load(user.getPhotoUrl()).circleCrop().into(imgProfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
view.findViewById(R.id.btnLogout).setOnClickListener(v -> {
|
||||||
|
FirebaseAuth.getInstance().signOut();
|
||||||
|
Intent intent = new Intent(getActivity(), MainActivity.class);
|
||||||
|
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,24 +5,24 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.example.pap_teste.models.Reserva;
|
import com.example.pap_teste.models.Reserva;
|
||||||
|
import com.google.android.material.chip.Chip;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
public class ReservaAdapter extends RecyclerView.Adapter<ReservaAdapter.ViewHolder> {
|
public class ReservaAdapter extends RecyclerView.Adapter<ReservaAdapter.ViewHolder> {
|
||||||
|
|
||||||
public interface OnReservaActionListener {
|
public interface OnReservaActionListener {
|
||||||
void onCheckIn(Reserva reserva);
|
void onCheckIn(Reserva reserva);
|
||||||
|
|
||||||
void onCancel(Reserva reserva);
|
void onCancel(Reserva reserva);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final List<Reserva> reservas;
|
private final List<Reserva> reservas;
|
||||||
private final OnReservaActionListener listener;
|
private final OnReservaActionListener listener;
|
||||||
|
private final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault());
|
||||||
|
|
||||||
public ReservaAdapter(List<Reserva> reservas, OnReservaActionListener listener) {
|
public ReservaAdapter(List<Reserva> reservas, OnReservaActionListener listener) {
|
||||||
this.reservas = reservas;
|
this.reservas = reservas;
|
||||||
@@ -39,64 +39,40 @@ public class ReservaAdapter extends RecyclerView.Adapter<ReservaAdapter.ViewHold
|
|||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
Reserva reserva = reservas.get(position);
|
Reserva reserva = reservas.get(position);
|
||||||
holder.txtRestaurante.setText(reserva.getRestauranteName());
|
|
||||||
holder.txtDataHora.setText(reserva.getData() + " às " + reserva.getHora() + " • " + reserva.getPessoas() + "p");
|
|
||||||
|
|
||||||
if (holder.chipStatus != null) {
|
holder.txtRestaurante.setText(reserva.getRestaurantName());
|
||||||
String state = reserva.getEstado() != null ? reserva.getEstado() : "Pendente";
|
|
||||||
holder.chipStatus.setText(state);
|
String dateStr = reserva.getDate() != null ? dateFormat.format(reserva.getDate().toDate()) : "Data N/A";
|
||||||
|
holder.txtDataHora.setText(String.format("%s às %s • %d pessoas", dateStr, reserva.getTimeSlot(), reserva.getGuests()));
|
||||||
|
|
||||||
|
String status = reserva.getStatus() != null ? reserva.getStatus() : "pending";
|
||||||
|
holder.chipStatus.setText(status.toUpperCase());
|
||||||
|
|
||||||
|
// Status Colors
|
||||||
int colorBg, colorText;
|
int colorBg, colorText;
|
||||||
android.content.Context ctx = holder.itemView.getContext();
|
android.content.Context ctx = holder.itemView.getContext();
|
||||||
if ("Confirmada".equals(state)) {
|
if ("confirmed".equals(status)) {
|
||||||
colorBg = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipConfirmed);
|
colorBg = ctx.getColor(R.color.colorChipConfirmed);
|
||||||
colorText = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipConfirmedText);
|
colorText = ctx.getColor(R.color.colorChipConfirmedText);
|
||||||
} else if ("Cancelada".equals(state) || "Recusada".equals(state)) {
|
holder.btnCheckIn.setVisibility(View.VISIBLE);
|
||||||
colorBg = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipCancelled);
|
} else if ("cancelled".equals(status) || "rejected".equals(status)) {
|
||||||
colorText = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipCancelledText);
|
colorBg = ctx.getColor(R.color.colorChipCancelled);
|
||||||
|
colorText = ctx.getColor(R.color.colorChipCancelledText);
|
||||||
|
holder.btnCheckIn.setVisibility(View.GONE);
|
||||||
} else {
|
} else {
|
||||||
colorBg = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipPending);
|
colorBg = ctx.getColor(R.color.colorChipPending);
|
||||||
colorText = androidx.core.content.ContextCompat.getColor(ctx, R.color.colorChipPendingText);
|
colorText = ctx.getColor(R.color.colorChipPendingText);
|
||||||
|
holder.btnCheckIn.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.chipStatus.setChipBackgroundColor(android.content.res.ColorStateList.valueOf(colorBg));
|
holder.chipStatus.setChipBackgroundColor(android.content.res.ColorStateList.valueOf(colorBg));
|
||||||
holder.chipStatus.setTextColor(colorText);
|
holder.chipStatus.setTextColor(colorText);
|
||||||
}
|
|
||||||
|
|
||||||
// Enable check-in only if confirmed
|
boolean canCancel = "pending".equals(status) || "confirmed".equals(status);
|
||||||
boolean isConfirmed = "Confirmada".equals(reserva.getEstado());
|
|
||||||
boolean hasLocationPermission = androidx.core.app.ActivityCompat.checkSelfPermission(holder.itemView.getContext(), android.Manifest.permission.ACCESS_FINE_LOCATION) == android.content.pm.PackageManager.PERMISSION_GRANTED;
|
|
||||||
|
|
||||||
if (isConfirmed) {
|
|
||||||
if (hasLocationPermission) {
|
|
||||||
holder.btnCheckIn.setEnabled(true);
|
|
||||||
if (holder.txtLocationWarning != null) {
|
|
||||||
holder.txtLocationWarning.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
holder.btnCheckIn.setEnabled(false);
|
|
||||||
if (holder.txtLocationWarning != null) {
|
|
||||||
holder.txtLocationWarning.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
holder.btnCheckIn.setEnabled(false);
|
|
||||||
if (holder.txtLocationWarning != null) {
|
|
||||||
holder.txtLocationWarning.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show cancel only if not concluded or already cancelled/refused
|
|
||||||
boolean canCancel = "Pendente".equals(reserva.getEstado()) || "Confirmada".equals(reserva.getEstado());
|
|
||||||
holder.btnCancelar.setVisibility(canCancel ? View.VISIBLE : View.GONE);
|
holder.btnCancelar.setVisibility(canCancel ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
holder.btnCheckIn.setOnClickListener(v -> {
|
holder.btnCheckIn.setOnClickListener(v -> listener.onCheckIn(reserva));
|
||||||
if (listener != null)
|
holder.btnCancelar.setOnClickListener(v -> listener.onCancel(reserva));
|
||||||
listener.onCheckIn(reserva);
|
|
||||||
});
|
|
||||||
|
|
||||||
holder.btnCancelar.setOnClickListener(v -> {
|
|
||||||
if (listener != null)
|
|
||||||
listener.onCancel(reserva);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -105,19 +81,18 @@ public class ReservaAdapter extends RecyclerView.Adapter<ReservaAdapter.ViewHold
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
TextView txtRestaurante, txtDataHora, txtStatus, txtLocationWarning;
|
TextView txtRestaurante, txtDataHora;
|
||||||
Button btnCheckIn, btnCancelar;
|
Button btnCheckIn, btnCancelar;
|
||||||
com.google.android.material.chip.Chip chipStatus;
|
Chip chipStatus;
|
||||||
|
|
||||||
public ViewHolder(@NonNull View itemView) {
|
public ViewHolder(@NonNull View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
txtRestaurante = itemView.findViewById(R.id.txtReservaRestaurante);
|
txtRestaurante = itemView.findViewById(R.id.txtReservaRestaurante);
|
||||||
txtDataHora = itemView.findViewById(R.id.txtReservaDataHora);
|
txtDataHora = itemView.findViewById(R.id.txtReservaDataHora);
|
||||||
txtStatus = itemView.findViewById(R.id.txtReservaStatus);
|
|
||||||
btnCheckIn = itemView.findViewById(R.id.btnCheckIn);
|
btnCheckIn = itemView.findViewById(R.id.btnCheckIn);
|
||||||
btnCancelar = itemView.findViewById(R.id.btnCancelar);
|
btnCancelar = itemView.findViewById(R.id.btnCancelar);
|
||||||
txtLocationWarning = itemView.findViewById(R.id.txtLocationWarning);
|
|
||||||
chipStatus = itemView.findViewById(R.id.chipReservaStatus);
|
chipStatus = itemView.findViewById(R.id.chipReservaStatus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
135
app/src/main/java/com/example/pap_teste/ReservationActivity.java
Normal file
135
app/src/main/java/com/example/pap_teste/ReservationActivity.java
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.app.DatePickerDialog;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.pap_teste.models.Reserva;
|
||||||
|
import com.example.pap_teste.models.Restaurant;
|
||||||
|
import com.google.firebase.Timestamp;
|
||||||
|
import com.google.firebase.auth.FirebaseAuth;
|
||||||
|
import com.google.firebase.firestore.FirebaseFirestore;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ReservationActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private String restaurantId, restaurantName;
|
||||||
|
private int guestCount = 2;
|
||||||
|
private Calendar selectedDate = Calendar.getInstance();
|
||||||
|
private String selectedTimeSlot = null;
|
||||||
|
|
||||||
|
private TextView txtGuestCount, txtSelectedDate;
|
||||||
|
private RecyclerView rvTimeSlots;
|
||||||
|
private TimeSlotAdapter adapter;
|
||||||
|
private List<String> timeSlots = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_reservation);
|
||||||
|
|
||||||
|
restaurantId = getIntent().getStringExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_ID);
|
||||||
|
restaurantName = getIntent().getStringExtra(RestaurantDetailActivity.EXTRA_RESTAURANT_NAME);
|
||||||
|
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setTitle("Reservar: " + restaurantName);
|
||||||
|
}
|
||||||
|
|
||||||
|
txtGuestCount = findViewById(R.id.txtGuestCount);
|
||||||
|
txtSelectedDate = findViewById(R.id.txtSelectedDate);
|
||||||
|
rvTimeSlots = findViewById(R.id.rvTimeSlots);
|
||||||
|
EditText etSpecialRequests = findViewById(R.id.etSpecialRequests);
|
||||||
|
|
||||||
|
findViewById(R.id.btnDecreaseGuests).setOnClickListener(v -> {
|
||||||
|
if (guestCount > 1) {
|
||||||
|
guestCount--;
|
||||||
|
txtGuestCount.setText(String.valueOf(guestCount));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.btnIncreaseGuests).setOnClickListener(v -> {
|
||||||
|
guestCount++;
|
||||||
|
txtGuestCount.setText(String.valueOf(guestCount));
|
||||||
|
});
|
||||||
|
|
||||||
|
findViewById(R.id.cardDatePicker).setOnClickListener(v -> showDatePicker());
|
||||||
|
|
||||||
|
setupTimeSlots();
|
||||||
|
loadRestaurantSlots();
|
||||||
|
|
||||||
|
findViewById(R.id.btnConfirmReservation).setOnClickListener(v -> {
|
||||||
|
if (selectedTimeSlot == null) {
|
||||||
|
Toast.makeText(this, "Por favor, selecione um horário.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
confirmReservation(etSpecialRequests.getText().toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDatePicker() {
|
||||||
|
new DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||||
|
selectedDate.set(year, month, dayOfMonth);
|
||||||
|
txtSelectedDate.setText(String.format("%02d/%02d/%d", dayOfMonth, month + 1, year));
|
||||||
|
}, selectedDate.get(Calendar.YEAR), selectedDate.get(Calendar.MONTH), selectedDate.get(Calendar.DAY_OF_MONTH)).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupTimeSlots() {
|
||||||
|
adapter = new TimeSlotAdapter(timeSlots, slot -> selectedTimeSlot = slot);
|
||||||
|
rvTimeSlots.setAdapter(adapter);
|
||||||
|
rvTimeSlots.setLayoutManager(new GridLayoutManager(this, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadRestaurantSlots() {
|
||||||
|
// In a real app, we would fetch from Firestore based on the selected date
|
||||||
|
// For now, let's use the static slots from the restaurant model
|
||||||
|
FirebaseFirestore.getInstance().collection("restaurants").document(restaurantId).get()
|
||||||
|
.addOnSuccessListener(documentSnapshot -> {
|
||||||
|
Restaurant r = documentSnapshot.toObject(Restaurant.class);
|
||||||
|
if (r != null && r.getAvailableSlots() != null) {
|
||||||
|
timeSlots.clear();
|
||||||
|
timeSlots.addAll(r.getAvailableSlots());
|
||||||
|
adapter.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void confirmReservation(String specialRequests) {
|
||||||
|
String userId = FirebaseAuth.getInstance().getUid();
|
||||||
|
if (userId == null) {
|
||||||
|
Toast.makeText(this, "Erro de autenticação.", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Reserva reserva = new Reserva();
|
||||||
|
reserva.setUserId(userId);
|
||||||
|
reserva.setRestaurantId(restaurantId);
|
||||||
|
reserva.setRestaurantName(restaurantName);
|
||||||
|
reserva.setGuests(guestCount);
|
||||||
|
reserva.setDate(new Timestamp(selectedDate.getTime()));
|
||||||
|
reserva.setTimeSlot(selectedTimeSlot);
|
||||||
|
reserva.setSpecialRequests(specialRequests);
|
||||||
|
reserva.setStatus("confirmed");
|
||||||
|
reserva.setCreatedAt(new Timestamp(new Date()));
|
||||||
|
|
||||||
|
FirebaseFirestore.getInstance().collection("reservations").add(reserva)
|
||||||
|
.addOnSuccessListener(documentReference -> {
|
||||||
|
Toast.makeText(this, "Reserva confirmada com sucesso!", Toast.LENGTH_LONG).show();
|
||||||
|
finish();
|
||||||
|
})
|
||||||
|
.addOnFailureListener(e -> Toast.makeText(this, "Erro ao confirmar reserva.", Toast.LENGTH_SHORT).show());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,132 +3,54 @@ package com.example.pap_teste;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.ImageView;
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
import com.example.pap_teste.models.Restaurant;
|
import com.example.pap_teste.models.Restaurant;
|
||||||
import com.google.firebase.auth.FirebaseAuth;
|
|
||||||
import com.google.firebase.auth.FirebaseUser;
|
|
||||||
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.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class RestaurantAdapter extends RecyclerView.Adapter<RestaurantAdapter.ViewHolder> {
|
public class RestaurantAdapter extends RecyclerView.Adapter<RestaurantAdapter.ViewHolder> {
|
||||||
private List<Restaurant> restaurants;
|
|
||||||
private OnRestaurantClickListener listener;
|
private final List<Restaurant> restaurants;
|
||||||
|
private final boolean isFeatured;
|
||||||
|
private final OnRestaurantClickListener listener;
|
||||||
|
|
||||||
public interface OnRestaurantClickListener {
|
public interface OnRestaurantClickListener {
|
||||||
void onRestaurantClick(Restaurant restaurant);
|
void onRestaurantClick(Restaurant restaurant);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RestaurantAdapter(List<Restaurant> restaurants, OnRestaurantClickListener listener) {
|
public RestaurantAdapter(List<Restaurant> restaurants, OnRestaurantClickListener listener) {
|
||||||
|
this(restaurants, false, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestaurantAdapter(List<Restaurant> restaurants, boolean isFeatured, OnRestaurantClickListener listener) {
|
||||||
this.restaurants = restaurants;
|
this.restaurants = restaurants;
|
||||||
|
this.isFeatured = isFeatured;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_restaurant, parent, false);
|
int layoutId = isFeatured ? R.layout.item_restaurant_featured : R.layout.item_restaurant_card;
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
|
||||||
return new ViewHolder(view);
|
return new ViewHolder(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
Restaurant restaurant = restaurants.get(position);
|
Restaurant restaurant = restaurants.get(position);
|
||||||
holder.text1.setText(restaurant.getName());
|
holder.name.setText(restaurant.getName());
|
||||||
holder.text2
|
holder.cuisine.setText(String.format("%s • %.1f ★", restaurant.getCuisine(), restaurant.getRating()));
|
||||||
.setText(restaurant.getCategory() + (restaurant.isAvailable() ? " - Disponível" : " - Indisponível"));
|
|
||||||
|
|
||||||
if (holder.txtRating != null) {
|
Glide.with(holder.itemView.getContext())
|
||||||
if (restaurant.getRatingAvg() != null && restaurant.getRatingAvg() > 0) {
|
.load(restaurant.getImageURL())
|
||||||
holder.txtRating
|
.placeholder(R.drawable.na_mesa)
|
||||||
.setText(String.format(java.util.Locale.getDefault(), "%.1f", restaurant.getRatingAvg()));
|
.into(holder.image);
|
||||||
} else {
|
|
||||||
holder.txtRating.setText("Novo");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (restaurant.getLogoUrl() != null && !restaurant.getLogoUrl().isEmpty()) {
|
holder.itemView.setOnClickListener(v -> listener.onRestaurantClick(restaurant));
|
||||||
com.bumptech.glide.Glide.with(holder.itemView.getContext())
|
|
||||||
.load(restaurant.getLogoUrl())
|
|
||||||
.circleCrop()
|
|
||||||
.into(holder.imgThumb);
|
|
||||||
} else {
|
|
||||||
holder.imgThumb.setImageResource(R.mipmap.ic_launcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.itemView.setOnClickListener(v -> {
|
|
||||||
if (listener != null) {
|
|
||||||
listener.onRestaurantClick(restaurant);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (holder.btnReservar != null) {
|
|
||||||
holder.btnReservar.setOnClickListener(v -> {
|
|
||||||
if (listener != null) {
|
|
||||||
listener.onRestaurantClick(restaurant);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
|
|
||||||
if (user != null && user.getEmail() != null && restaurant.getEmail() != null) {
|
|
||||||
String encodedUserEmail = user.getEmail().replace(".", "_").replace("@", "_at_");
|
|
||||||
String encodedRestEmail = restaurant.getEmail().replace(".", "_").replace("@", "_at_");
|
|
||||||
DatabaseReference favRef = FirebaseDatabase.getInstance().getReference("Clientes")
|
|
||||||
.child(encodedUserEmail).child("favorites").child(encodedRestEmail);
|
|
||||||
|
|
||||||
// Read initial state
|
|
||||||
favRef.addListenerForSingleValueEvent(new ValueEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onDataChange(@NonNull DataSnapshot snapshot) {
|
|
||||||
holder.btnFavorite.setTag(snapshot.exists());
|
|
||||||
if (snapshot.exists()) {
|
|
||||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
|
||||||
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
|
||||||
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
|
||||||
} else {
|
|
||||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
|
||||||
holder.btnFavorite.clearColorFilter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancelled(@NonNull DatabaseError error) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
holder.btnFavorite.setOnClickListener(v -> {
|
|
||||||
// Optimistic UI update
|
|
||||||
boolean isFav = holder.btnFavorite.getTag() != null && (Boolean) holder.btnFavorite.getTag();
|
|
||||||
boolean newFavState = !isFav;
|
|
||||||
holder.btnFavorite.setTag(newFavState);
|
|
||||||
|
|
||||||
if (newFavState) {
|
|
||||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_on);
|
|
||||||
holder.btnFavorite.setColorFilter(androidx.core.content.ContextCompat
|
|
||||||
.getColor(holder.itemView.getContext(), R.color.colorPrimary));
|
|
||||||
favRef.setValue(restaurant);
|
|
||||||
com.google.android.material.snackbar.Snackbar.make(v, "Adicionado aos Favoritos!",
|
|
||||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
|
||||||
} else {
|
|
||||||
holder.btnFavorite.setImageResource(android.R.drawable.btn_star_big_off);
|
|
||||||
holder.btnFavorite.clearColorFilter();
|
|
||||||
favRef.removeValue();
|
|
||||||
com.google.android.material.snackbar.Snackbar.make(v, "Removido dos Favoritos.",
|
|
||||||
com.google.android.material.snackbar.Snackbar.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -136,20 +58,16 @@ public class RestaurantAdapter extends RecyclerView.Adapter<RestaurantAdapter.Vi
|
|||||||
return restaurants.size();
|
return restaurants.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
TextView text1, text2, txtRating;
|
ImageView image;
|
||||||
ImageButton btnFavorite;
|
TextView name;
|
||||||
android.widget.ImageView imgThumb;
|
TextView cuisine;
|
||||||
Button btnReservar;
|
|
||||||
|
|
||||||
public ViewHolder(@NonNull View itemView) {
|
ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
text1 = itemView.findViewById(R.id.txtRestaurantName);
|
image = itemView.findViewById(R.id.restaurantImage);
|
||||||
text2 = itemView.findViewById(R.id.txtRestaurantCategory);
|
name = itemView.findViewById(R.id.restaurantName);
|
||||||
txtRating = itemView.findViewById(R.id.txtRestaurantRating);
|
cuisine = itemView.findViewById(R.id.restaurantCuisine);
|
||||||
btnFavorite = itemView.findViewById(R.id.btnFavorite);
|
|
||||||
imgThumb = itemView.findViewById(R.id.imgRestaurantThumb);
|
|
||||||
btnReservar = itemView.findViewById(R.id.btnReservarCard);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
|
public class RestaurantDetailActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
public static final String EXTRA_RESTAURANT_ID = "extra_restaurant_id";
|
||||||
|
public static final String EXTRA_RESTAURANT_NAME = "extra_restaurant_name";
|
||||||
|
public static final String EXTRA_RESTAURANT_IMAGE = "extra_restaurant_image";
|
||||||
|
public static final String EXTRA_RESTAURANT_CUISINE = "extra_restaurant_cuisine";
|
||||||
|
public static final String EXTRA_RESTAURANT_RATING = "extra_restaurant_rating";
|
||||||
|
public static final String EXTRA_RESTAURANT_DESC = "extra_restaurant_desc";
|
||||||
|
public static final String EXTRA_RESTAURANT_ADDRESS = "extra_restaurant_address";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_restaurant_detail);
|
||||||
|
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setTitle("");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageView coverImage = findViewById(R.id.restaurantCover);
|
||||||
|
TextView nameText = findViewById(R.id.detailName);
|
||||||
|
TextView cuisineText = findViewById(R.id.detailCuisine);
|
||||||
|
TextView ratingText = findViewById(R.id.detailRating);
|
||||||
|
TextView descText = findViewById(R.id.detailDescription);
|
||||||
|
TextView addressText = findViewById(R.id.detailAddress);
|
||||||
|
MaterialButton btnReserve = findViewById(R.id.btnReserveNow);
|
||||||
|
|
||||||
|
// Get data from intent
|
||||||
|
String name = getIntent().getStringExtra(EXTRA_RESTAURANT_NAME);
|
||||||
|
String image = getIntent().getStringExtra(EXTRA_RESTAURANT_IMAGE);
|
||||||
|
String cuisine = getIntent().getStringExtra(EXTRA_RESTAURANT_CUISINE);
|
||||||
|
double rating = getIntent().getDoubleExtra(EXTRA_RESTAURANT_RATING, 0.0);
|
||||||
|
String desc = getIntent().getStringExtra(EXTRA_RESTAURANT_DESC);
|
||||||
|
String address = getIntent().getStringExtra(EXTRA_RESTAURANT_ADDRESS);
|
||||||
|
|
||||||
|
nameText.setText(name);
|
||||||
|
cuisineText.setText(cuisine);
|
||||||
|
ratingText.setText(String.format("%.1f ★", rating));
|
||||||
|
descText.setText(desc);
|
||||||
|
addressText.setText(address);
|
||||||
|
|
||||||
|
Glide.with(this).load(image).into(coverImage);
|
||||||
|
|
||||||
|
btnReserve.setOnClickListener(v -> {
|
||||||
|
Intent intent = new Intent(this, ReservationActivity.class);
|
||||||
|
intent.putExtra(EXTRA_RESTAURANT_ID, getIntent().getStringExtra(EXTRA_RESTAURANT_ID));
|
||||||
|
intent.putExtra(EXTRA_RESTAURANT_NAME, getIntent().getStringExtra(EXTRA_RESTAURANT_NAME));
|
||||||
|
startActivity(intent);
|
||||||
|
});
|
||||||
|
|
||||||
|
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||||
|
}
|
||||||
|
}
|
||||||
36
app/src/main/java/com/example/pap_teste/SplashActivity.java
Normal file
36
app/src/main/java/com/example/pap_teste/SplashActivity.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.AnimationUtils;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
public class SplashActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_splash);
|
||||||
|
|
||||||
|
ImageView logo = findViewById(R.id.splash_logo);
|
||||||
|
TextView title = findViewById(R.id.splash_title);
|
||||||
|
TextView subtitle = findViewById(R.id.splash_subtitle);
|
||||||
|
|
||||||
|
Animation fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
|
||||||
|
fadeIn.setDuration(1500);
|
||||||
|
|
||||||
|
logo.startAnimation(fadeIn);
|
||||||
|
title.startAnimation(fadeIn);
|
||||||
|
subtitle.startAnimation(fadeIn);
|
||||||
|
|
||||||
|
new Handler(Looper.getMainLooper()).postDelayed(() -> {
|
||||||
|
startActivity(new Intent(SplashActivity.this, OnboardingActivity.class));
|
||||||
|
finish();
|
||||||
|
}, 2500);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.example.pap_teste.models.Mesa;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TableSelectionAdapter extends RecyclerView.Adapter<TableSelectionAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
private final List<Mesa> tables;
|
||||||
|
private final OnTableClickListener listener;
|
||||||
|
private int selectedPosition = -1;
|
||||||
|
|
||||||
|
public interface OnTableClickListener {
|
||||||
|
void onTableClick(Mesa mesa);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TableSelectionAdapter(List<Mesa> tables, OnTableClickListener listener) {
|
||||||
|
this.tables = tables;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_table, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
Mesa mesa = tables.get(position);
|
||||||
|
holder.txtNumber.setText("Mesa " + mesa.getNumero());
|
||||||
|
|
||||||
|
boolean isOccupied = "OCUPADA".equalsIgnoreCase(mesa.getEstado()) || "RESERVADA".equalsIgnoreCase(mesa.getEstado());
|
||||||
|
holder.txtStatus.setText(mesa.getEstado());
|
||||||
|
|
||||||
|
if (isOccupied) {
|
||||||
|
holder.card.setCardBackgroundColor(Color.parseColor("#F5F5F5"));
|
||||||
|
holder.card.setStrokeColor(Color.TRANSPARENT);
|
||||||
|
holder.txtStatus.setTextColor(Color.GRAY);
|
||||||
|
holder.itemView.setEnabled(false);
|
||||||
|
holder.imgStatus.setAlpha(0.3f);
|
||||||
|
} else {
|
||||||
|
boolean isSelected = selectedPosition == position;
|
||||||
|
holder.card.setCardBackgroundColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorChipConfirmed) : Color.WHITE);
|
||||||
|
holder.card.setStrokeColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorSuccess) : holder.itemView.getContext().getColor(R.color.colorBorder));
|
||||||
|
holder.txtStatus.setTextColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorSuccess) : holder.itemView.getContext().getColor(R.color.colorTextSecondary));
|
||||||
|
holder.itemView.setEnabled(true);
|
||||||
|
holder.imgStatus.setAlpha(1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.itemView.setOnClickListener(v -> {
|
||||||
|
int previousSelected = selectedPosition;
|
||||||
|
selectedPosition = holder.getAdapterPosition();
|
||||||
|
if (previousSelected != -1) notifyItemChanged(previousSelected);
|
||||||
|
notifyItemChanged(selectedPosition);
|
||||||
|
listener.onTableClick(mesa);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return tables.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
MaterialCardView card;
|
||||||
|
TextView txtNumber, txtStatus;
|
||||||
|
ImageView imgStatus;
|
||||||
|
|
||||||
|
ViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
card = itemView.findViewById(R.id.cardTable);
|
||||||
|
txtNumber = itemView.findViewById(R.id.txtTableNumber);
|
||||||
|
txtStatus = itemView.findViewById(R.id.txtTableStatus);
|
||||||
|
imgStatus = itemView.findViewById(R.id.imgTableStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
app/src/main/java/com/example/pap_teste/TimeSlotAdapter.java
Normal file
76
app/src/main/java/com/example/pap_teste/TimeSlotAdapter.java
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package com.example.pap_teste;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import com.google.android.material.card.MaterialCardView;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TimeSlotAdapter extends RecyclerView.Adapter<TimeSlotAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
private final List<String> timeSlots;
|
||||||
|
private final OnTimeSlotClickListener listener;
|
||||||
|
private int selectedPosition = -1;
|
||||||
|
|
||||||
|
public interface OnTimeSlotClickListener {
|
||||||
|
void onTimeSlotClick(String slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeSlotAdapter(List<String> timeSlots, OnTimeSlotClickListener listener) {
|
||||||
|
this.timeSlots = timeSlots;
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_time_slot, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
|
String slot = timeSlots.get(position);
|
||||||
|
holder.txtTime.setText(slot);
|
||||||
|
|
||||||
|
boolean isSelected = selectedPosition == position;
|
||||||
|
holder.card.setStrokeColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorPrimary) : holder.itemView.getContext().getColor(R.color.colorBorder));
|
||||||
|
holder.card.setStrokeWidth(isSelected ? 4 : 2);
|
||||||
|
holder.card.setCardBackgroundColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorChip) : Color.WHITE);
|
||||||
|
holder.txtTime.setTextColor(isSelected ? holder.itemView.getContext().getColor(R.color.colorPrimary) : holder.itemView.getContext().getColor(R.color.colorTextPrimary));
|
||||||
|
|
||||||
|
holder.itemView.setOnClickListener(v -> {
|
||||||
|
int previousSelected = selectedPosition;
|
||||||
|
int currentPos = holder.getAdapterPosition();
|
||||||
|
|
||||||
|
if (currentPos != RecyclerView.NO_POSITION) {
|
||||||
|
selectedPosition = currentPos;
|
||||||
|
if (previousSelected != -1) {
|
||||||
|
notifyItemChanged(previousSelected);
|
||||||
|
}
|
||||||
|
notifyItemChanged(selectedPosition);
|
||||||
|
listener.onTimeSlotClick(slot);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return timeSlots.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
MaterialCardView card;
|
||||||
|
TextView txtTime;
|
||||||
|
|
||||||
|
ViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
card = itemView.findViewById(R.id.cardTimeSlot);
|
||||||
|
txtTime = itemView.findViewById(R.id.txtTimeSlot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,6 +51,9 @@ public class InteractiveRatingBar extends LinearLayout {
|
|||||||
int sizePx = (int) (starSizeDp * context.getResources().getDisplayMetrics().density);
|
int sizePx = (int) (starSizeDp * context.getResources().getDisplayMetrics().density);
|
||||||
int paddingPx = (int) (starPaddingDp * context.getResources().getDisplayMetrics().density);
|
int paddingPx = (int) (starPaddingDp * context.getResources().getDisplayMetrics().density);
|
||||||
|
|
||||||
|
stars.clear();
|
||||||
|
removeAllViews();
|
||||||
|
|
||||||
for (int i = 0; i < numStars; i++) {
|
for (int i = 0; i < numStars; i++) {
|
||||||
ImageView star = new ImageView(context);
|
ImageView star = new ImageView(context);
|
||||||
LayoutParams params = new LayoutParams(sizePx, sizePx);
|
LayoutParams params = new LayoutParams(sizePx, sizePx);
|
||||||
@@ -91,11 +94,12 @@ public class InteractiveRatingBar extends LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void calculateRatingFromTouch(float x) {
|
private void calculateRatingFromTouch(float x) {
|
||||||
if (stars.isEmpty()) return;
|
int count = stars.size();
|
||||||
|
if (count == 0) return;
|
||||||
|
|
||||||
double newRating = 0;
|
double newRating = 0;
|
||||||
|
|
||||||
for (int i = 0; i < numStars; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
ImageView star = stars.get(i);
|
ImageView star = stars.get(i);
|
||||||
float starLeft = star.getLeft();
|
float starLeft = star.getLeft();
|
||||||
float starRight = star.getRight();
|
float starRight = star.getRight();
|
||||||
@@ -116,7 +120,7 @@ public class InteractiveRatingBar extends LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clamp values
|
// Clamp values
|
||||||
newRating = Math.max(0.5, Math.min(newRating, numStars));
|
newRating = Math.max(0.5, Math.min(newRating, (double) count));
|
||||||
|
|
||||||
if (newRating != currentRating) {
|
if (newRating != currentRating) {
|
||||||
currentRating = newRating;
|
currentRating = newRating;
|
||||||
@@ -128,7 +132,8 @@ public class InteractiveRatingBar extends LinearLayout {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateStars(double rating, boolean animate) {
|
private void updateStars(double rating, boolean animate) {
|
||||||
for (int i = 0; i < numStars; i++) {
|
int count = stars.size();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
ImageView star = stars.get(i);
|
ImageView star = stars.get(i);
|
||||||
int previousResId = (Integer) (star.getTag() != null ? star.getTag() : 0);
|
int previousResId = (Integer) (star.getTag() != null ? star.getTag() : 0);
|
||||||
int newResId;
|
int newResId;
|
||||||
|
|||||||
@@ -3,17 +3,21 @@ package com.example.pap_teste.models;
|
|||||||
public class FoodCategory {
|
public class FoodCategory {
|
||||||
private String name;
|
private String name;
|
||||||
private int imageResId;
|
private int imageResId;
|
||||||
|
private String imageUrl;
|
||||||
|
|
||||||
|
public FoodCategory() {}
|
||||||
|
|
||||||
public FoodCategory(String name, int imageResId) {
|
public FoodCategory(String name, int imageResId) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.imageResId = imageResId;
|
this.imageResId = imageResId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() { return name; }
|
||||||
return name;
|
public void setName(String name) { this.name = name; }
|
||||||
}
|
|
||||||
|
|
||||||
public int getImageResId() {
|
public int getImageResId() { return imageResId; }
|
||||||
return imageResId;
|
public void setImageResId(int imageResId) { this.imageResId = imageResId; }
|
||||||
}
|
|
||||||
|
public String getImageUrl() { return imageUrl; }
|
||||||
|
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package com.example.pap_teste.models;
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
import com.google.firebase.Timestamp;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
public class Reserva {
|
public class Reserva {
|
||||||
private String id;
|
private String id;
|
||||||
private String clienteEmail;
|
private String clienteEmail;
|
||||||
@@ -7,15 +10,18 @@ public class Reserva {
|
|||||||
private String restauranteEmail;
|
private String restauranteEmail;
|
||||||
private String data;
|
private String data;
|
||||||
private String hora;
|
private String hora;
|
||||||
|
private com.google.firebase.Timestamp date;
|
||||||
private int pessoas;
|
private int pessoas;
|
||||||
private String estado; // Pendente, Confirmada, Concluída, Cancelada, Recusada
|
private int tableNumber;
|
||||||
|
private String estado; // "Pendente", "Confirmada", "Cancelada"
|
||||||
|
private String specialRequests;
|
||||||
|
private Timestamp createdAt;
|
||||||
|
|
||||||
public Reserva() {
|
public Reserva() {
|
||||||
// Required for Firebase
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Reserva(String id, String clienteEmail, String restauranteName, String restauranteEmail, String data,
|
public Reserva(String id, String clienteEmail, String restauranteName, String restauranteEmail,
|
||||||
String hora, int pessoas, String estado) {
|
String data, String hora, int pessoas, String estado) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.clienteEmail = clienteEmail;
|
this.clienteEmail = clienteEmail;
|
||||||
this.restauranteName = restauranteName;
|
this.restauranteName = restauranteName;
|
||||||
@@ -24,69 +30,56 @@ public class Reserva {
|
|||||||
this.hora = hora;
|
this.hora = hora;
|
||||||
this.pessoas = pessoas;
|
this.pessoas = pessoas;
|
||||||
this.estado = estado;
|
this.estado = estado;
|
||||||
|
this.createdAt = new Timestamp(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() { return id; }
|
||||||
return id;
|
public void setId(String id) { this.id = id; }
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(String id) {
|
public String getClienteEmail() { return clienteEmail; }
|
||||||
this.id = id;
|
public void setClienteEmail(String clienteEmail) { this.clienteEmail = clienteEmail; }
|
||||||
}
|
|
||||||
|
|
||||||
public String getClienteEmail() {
|
public String getRestauranteName() { return restauranteName; }
|
||||||
return clienteEmail;
|
public void setRestauranteName(String restauranteName) { this.restauranteName = restauranteName; }
|
||||||
}
|
|
||||||
|
|
||||||
public void setClienteEmail(String clienteEmail) {
|
public String getRestauranteEmail() { return restauranteEmail; }
|
||||||
this.clienteEmail = clienteEmail;
|
public void setRestauranteEmail(String restauranteEmail) { this.restauranteEmail = restauranteEmail; }
|
||||||
}
|
|
||||||
|
|
||||||
public String getRestauranteName() {
|
public String getData() { return data; }
|
||||||
return restauranteName;
|
public void setData(String data) { this.data = data; }
|
||||||
}
|
|
||||||
|
|
||||||
public void setRestauranteName(String restauranteName) {
|
public String getHora() { return hora; }
|
||||||
this.restauranteName = restauranteName;
|
public void setHora(String hora) { this.hora = hora; }
|
||||||
}
|
|
||||||
|
|
||||||
public String getRestauranteEmail() {
|
public com.google.firebase.Timestamp getDate() { return date; }
|
||||||
return restauranteEmail;
|
public void setDate(com.google.firebase.Timestamp date) { this.date = date; }
|
||||||
}
|
|
||||||
|
|
||||||
public void setRestauranteEmail(String restauranteEmail) {
|
public int getPessoas() { return pessoas; }
|
||||||
this.restauranteEmail = restauranteEmail;
|
public void setPessoas(int pessoas) { this.pessoas = pessoas; }
|
||||||
}
|
|
||||||
|
|
||||||
public String getData() {
|
public int getTableNumber() { return tableNumber; }
|
||||||
return data;
|
public void setTableNumber(int tableNumber) { this.tableNumber = tableNumber; }
|
||||||
}
|
|
||||||
|
|
||||||
public void setData(String data) {
|
public String getEstado() { return estado; }
|
||||||
this.data = data;
|
public void setEstado(String estado) { this.estado = estado; }
|
||||||
}
|
|
||||||
|
|
||||||
public String getHora() {
|
public String getSpecialRequests() { return specialRequests; }
|
||||||
return hora;
|
public void setSpecialRequests(String specialRequests) { this.specialRequests = specialRequests; }
|
||||||
}
|
|
||||||
|
|
||||||
public void setHora(String hora) {
|
public Timestamp getCreatedAt() { return createdAt; }
|
||||||
this.hora = hora;
|
public void setCreatedAt(Timestamp createdAt) { this.createdAt = createdAt; }
|
||||||
}
|
|
||||||
|
|
||||||
public int getPessoas() {
|
// Compatibility mappings for ReservationActivity
|
||||||
return pessoas;
|
public String getUserId() { return clienteEmail; }
|
||||||
}
|
public void setUserId(String userId) { this.clienteEmail = userId; }
|
||||||
|
public String getRestaurantId() { return restauranteEmail; }
|
||||||
public void setPessoas(int pessoas) {
|
public void setRestaurantId(String restaurantId) { this.restauranteEmail = restaurantId; }
|
||||||
this.pessoas = pessoas;
|
public String getRestaurantName() { return restauranteName; }
|
||||||
}
|
public void setRestaurantName(String restaurantName) { this.restauranteName = restaurantName; }
|
||||||
|
public int getGuests() { return pessoas; }
|
||||||
public String getEstado() {
|
public void setGuests(int guests) { this.pessoas = guests; }
|
||||||
return estado;
|
public String getStatus() { return estado; }
|
||||||
}
|
public void setStatus(String status) { this.estado = status; }
|
||||||
|
public String getTimeSlot() { return hora; }
|
||||||
public void setEstado(String estado) {
|
public void setTimeSlot(String timeSlot) { this.hora = timeSlot; }
|
||||||
this.estado = estado;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,75 @@
|
|||||||
package com.example.pap_teste.models;
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class Restaurant implements Serializable {
|
public class Restaurant {
|
||||||
|
private String id;
|
||||||
private String name;
|
private String name;
|
||||||
private String category;
|
private String category;
|
||||||
private String email;
|
private String email;
|
||||||
private boolean available;
|
private String description;
|
||||||
private String logoUrl;
|
private String imageURL;
|
||||||
|
private double rating;
|
||||||
private Double ratingAvg;
|
private Double ratingAvg;
|
||||||
private Integer ratingCount;
|
private Integer ratingCount;
|
||||||
|
private String address;
|
||||||
|
private Map<String, String> openingHours;
|
||||||
|
private List<String> availableSlots;
|
||||||
|
private boolean favorite;
|
||||||
|
|
||||||
// No-argument constructor required for Firebase
|
public Restaurant() {}
|
||||||
public Restaurant() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Restaurant(String name, String category, String email, boolean available) {
|
public Restaurant(String name, String category, String email, boolean favorite, String imageURL) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.category = category;
|
this.category = category;
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.available = available;
|
this.favorite = favorite;
|
||||||
|
this.imageURL = imageURL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Restaurant(String name, String category, String email, boolean available, String logoUrl) {
|
public String getId() { return id; }
|
||||||
this.name = name;
|
public void setId(String id) { this.id = id; }
|
||||||
this.category = category;
|
|
||||||
this.email = email;
|
|
||||||
this.available = available;
|
|
||||||
this.logoUrl = logoUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() { return name; }
|
public String getName() { return name; }
|
||||||
public String getCategory() { return category; }
|
|
||||||
public String getEmail() { return email; }
|
|
||||||
public boolean isAvailable() { return available; }
|
|
||||||
public String getLogoUrl() { return logoUrl; }
|
|
||||||
|
|
||||||
public void setName(String name) { this.name = name; }
|
public void setName(String name) { this.name = name; }
|
||||||
public void setCategory(String category) { this.category = category; }
|
|
||||||
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 Double getRatingAvg() { return ratingAvg; }
|
public String getCategory() { return category; }
|
||||||
|
public void setCategory(String category) { this.category = category; }
|
||||||
|
|
||||||
|
public String getEmail() { return email; }
|
||||||
|
public void setEmail(String email) { this.email = email; }
|
||||||
|
|
||||||
|
public String getDescription() { return description; }
|
||||||
|
public void setDescription(String description) { this.description = description; }
|
||||||
|
|
||||||
|
public String getImageURL() { return imageURL; }
|
||||||
|
public void setImageURL(String imageURL) { this.imageURL = imageURL; }
|
||||||
|
|
||||||
|
public double getRating() { return rating; }
|
||||||
|
public void setRating(double rating) { this.rating = rating; }
|
||||||
|
|
||||||
|
public Double getRatingAvg() { return ratingAvg != null ? ratingAvg : rating; }
|
||||||
public void setRatingAvg(Double ratingAvg) { this.ratingAvg = ratingAvg; }
|
public void setRatingAvg(Double ratingAvg) { this.ratingAvg = ratingAvg; }
|
||||||
public Integer getRatingCount() { return ratingCount; }
|
|
||||||
|
public Integer getRatingCount() { return ratingCount != null ? ratingCount : 0; }
|
||||||
public void setRatingCount(Integer ratingCount) { this.ratingCount = ratingCount; }
|
public void setRatingCount(Integer ratingCount) { this.ratingCount = ratingCount; }
|
||||||
|
|
||||||
|
public String getAddress() { return address; }
|
||||||
|
public void setAddress(String address) { this.address = address; }
|
||||||
|
|
||||||
|
public Map<String, String> getOpeningHours() { return openingHours; }
|
||||||
|
public void setOpeningHours(Map<String, String> openingHours) { this.openingHours = openingHours; }
|
||||||
|
|
||||||
|
public List<String> getAvailableSlots() { return availableSlots; }
|
||||||
|
public void setAvailableSlots(List<String> availableSlots) { this.availableSlots = availableSlots; }
|
||||||
|
|
||||||
|
public boolean isFavorite() { return favorite; }
|
||||||
|
public void setFavorite(boolean favorite) { this.favorite = favorite; }
|
||||||
|
|
||||||
|
// Compatibility mappings
|
||||||
|
public String getCuisine() { return category; }
|
||||||
|
public void setCuisine(String cuisine) { this.category = cuisine; }
|
||||||
|
public String getLogoUrl() { return imageURL; }
|
||||||
|
public void setLogoUrl(String logoUrl) { this.imageURL = logoUrl; }
|
||||||
}
|
}
|
||||||
|
|||||||
36
app/src/main/java/com/example/pap_teste/models/User.java
Normal file
36
app/src/main/java/com/example/pap_teste/models/User.java
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package com.example.pap_teste.models;
|
||||||
|
|
||||||
|
import com.google.firebase.Timestamp;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class User {
|
||||||
|
private String id;
|
||||||
|
private String displayName;
|
||||||
|
private String email;
|
||||||
|
private String photoURL;
|
||||||
|
private String role; // "CLIENTE" or "RESTAURANTE"
|
||||||
|
private Timestamp createdAt;
|
||||||
|
|
||||||
|
public User() {}
|
||||||
|
|
||||||
|
public User(String id, String displayName, String email, String role) {
|
||||||
|
this.id = id;
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.email = email;
|
||||||
|
this.role = role;
|
||||||
|
this.createdAt = new Timestamp(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() { return id; }
|
||||||
|
public void setId(String id) { this.id = id; }
|
||||||
|
public String getDisplayName() { return displayName; }
|
||||||
|
public void setDisplayName(String displayName) { this.displayName = displayName; }
|
||||||
|
public String getEmail() { return email; }
|
||||||
|
public void setEmail(String email) { this.email = email; }
|
||||||
|
public String getPhotoURL() { return photoURL; }
|
||||||
|
public void setPhotoURL(String photoURL) { this.photoURL = photoURL; }
|
||||||
|
public String getRole() { return role; }
|
||||||
|
public void setRole(String role) { this.role = role; }
|
||||||
|
public Timestamp getCreatedAt() { return createdAt; }
|
||||||
|
public void setCreatedAt(Timestamp createdAt) { this.createdAt = createdAt; }
|
||||||
|
}
|
||||||
7
app/src/main/res/drawable/gradient_overlay.xml
Normal file
7
app/src/main/res/drawable/gradient_overlay.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<gradient
|
||||||
|
android:angle="270"
|
||||||
|
android:endColor="#00000000"
|
||||||
|
android:startColor="#CC000000" />
|
||||||
|
</shape>
|
||||||
@@ -1,164 +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/main"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/colorBackground"
|
|
||||||
tools:context=".AddStaffActivity">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnVoltarAddStaff"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="← Voltar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorError"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtAdicionarTitulo"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:text="Novo Funcionário"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/btnVoltarAddStaff" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtAdicionarDesc"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="32dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Introduza os dados e atribuições deste membro:"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="14sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtAdicionarTitulo" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtAdicionarDesc">
|
|
||||||
|
|
||||||
<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="Nome do Funcionário"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/nammeEditText"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="Ex: João Silva"
|
|
||||||
android:inputType="textPersonName"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:textColor="@color/colorTextPrimary" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:text="Zona de Trabalho"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/zoneLayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:gravity="center_vertical">
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/zonaSpinner"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/input_bg" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnAddZone"
|
|
||||||
android:layout_width="54dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
app:backgroundTint="@color/colorPrimary"
|
|
||||||
android:insetTop="0dp"
|
|
||||||
android:insetBottom="0dp"
|
|
||||||
android:paddingTop="0dp"
|
|
||||||
android:paddingBottom="0dp"
|
|
||||||
android:text="+"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="24sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:text="Mesa a Atribuir"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/mesaLayout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/mesaSpinner"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:background="@drawable/input_bg" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/addButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_marginTop="36dp"
|
|
||||||
app:cornerRadius="14dp"
|
|
||||||
app:backgroundTint="@color/colorPrimary"
|
|
||||||
android:text="Adicionar Membro"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
android:background="@color/colorBackground"
|
android:background="@color/colorBackground"
|
||||||
tools:context=".ClientDashboardActivity">
|
tools:context=".ClientDashboardActivity">
|
||||||
|
|
||||||
<ScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/clientScroll"
|
android:id="@+id/clientScroll"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
@@ -36,13 +36,15 @@
|
|||||||
android:id="@+id/txtClientGreeting"
|
android:id="@+id/txtClientGreeting"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fontFamily="sans-serif"
|
|
||||||
android:text="Olá, convidado!"
|
android:text="Olá, convidado!"
|
||||||
android:textColor="@color/colorTextPrimary"
|
android:textColor="@color/colorTextPrimary"
|
||||||
android:textSize="28sp"
|
android:textSize="28sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintEnd_toStartOf="@id/imgNotification"
|
||||||
|
android:layout_marginEnd="16dp" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@@ -50,8 +52,21 @@
|
|||||||
android:text="Encontre a sua próxima mesa."
|
android:text="Encontre a sua próxima mesa."
|
||||||
android:textColor="@color/colorTextSecondary"
|
android:textColor="@color/colorTextSecondary"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/txtClientGreeting"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtClientGreeting" />
|
app:layout_constraintEnd_toStartOf="@id/imgNotification" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/imgNotification"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="@drawable/bg_circle_white"
|
||||||
|
android:src="@android:drawable/ic_popup_reminder"
|
||||||
|
app:tint="@color/colorTextPrimary"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/cardProfile"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/cardProfile"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/cardProfile" />
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/cardProfile"
|
android:id="@+id/cardProfile"
|
||||||
@@ -141,7 +156,7 @@
|
|||||||
android:visibility="visible"
|
android:visibility="visible"
|
||||||
android:indeterminateTint="@color/colorPrimary" />
|
android:indeterminateTint="@color/colorPrimary" />
|
||||||
|
|
||||||
<!-- Featured Carousel -->
|
<!-- Featured / Destaques -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layoutFeatured"
|
android:id="@+id/layoutFeatured"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -154,7 +169,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="24dp"
|
android:layout_marginStart="24dp"
|
||||||
android:text="Restaurantes Populares"
|
android:text="Destaques"
|
||||||
android:textColor="@color/colorTextPrimary"
|
android:textColor="@color/colorTextPrimary"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
@@ -173,9 +188,9 @@
|
|||||||
tools:listitem="@layout/item_restaurant_featured" />
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Main Restaurant Grid/List -->
|
<!-- Melhor Avaliados -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layoutAllRestaurants"
|
android:id="@+id/layoutTopRated"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
@@ -183,29 +198,191 @@
|
|||||||
android:layout_marginTop="32dp">
|
android:layout_marginTop="32dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtListTitle"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="24dp"
|
android:layout_marginStart="24dp"
|
||||||
android:text="Todos os Restaurantes"
|
android:text="Melhor Avaliados"
|
||||||
android:textColor="@color/colorTextPrimary"
|
android:textColor="@color/colorTextPrimary"
|
||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:fontFamily="sans-serif" />
|
android:fontFamily="sans-serif" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/rvMainRestaurants"
|
android:id="@+id/rvTopRated"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginTop="12dp"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingBottom="16dp"
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
tools:listitem="@layout/item_restaurant" />
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Pizzas -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutPizzas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginTop="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:text="Pizzas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvPizzas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Sushi -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutSushi"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginTop="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:text="Sushi"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvSushi"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Carnes -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutCarnes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginTop="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:text="Carnes"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvCarnes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Massas -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutMassas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginTop="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:text="Massas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvMassas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Sobremesas -->
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutSobremesas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:layout_marginTop="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:text="Sobremesas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:fontFamily="sans-serif" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvSobremesas"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingStart="24dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingBottom="16dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</ScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
<!-- Bottom Navigation Bar -->
|
<!-- Bottom Navigation Bar -->
|
||||||
<com.google.android.material.bottomnavigation.BottomNavigationView
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
|||||||
27
app/src/main/res/layout/activity_client_dashboard_v2.xml
Normal file
27
app/src/main/res/layout/activity_client_dashboard_v2.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/fragmentContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/bottomNavigation"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
|
android:id="@+id/bottomNavigation"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/white"
|
||||||
|
app:elevation="8dp"
|
||||||
|
app:itemIconTint="@color/colorPrimary"
|
||||||
|
app:itemTextColor="@color/colorPrimary"
|
||||||
|
app:labelVisibilityMode="labeled"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:menu="@menu/bottom_nav_menu" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -1,176 +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/definicoesRoot"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/colorBackground"
|
|
||||||
tools:context=".DefinicoesAdminActivity">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnVoltar"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="← Voltar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:fillViewport="true"
|
|
||||||
android:scrollbars="none"
|
|
||||||
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:paddingHorizontal="24dp"
|
|
||||||
android:paddingTop="8dp"
|
|
||||||
android:paddingBottom="48dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Definições do Estabelecimento"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
android:textAlignment="center" />
|
|
||||||
|
|
||||||
<!-- Improved Logo using MaterialCardView -->
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="120dp"
|
|
||||||
android:layout_height="120dp"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
app:cardCornerRadius="60dp"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:strokeWidth="1dp"
|
|
||||||
app:strokeColor="@color/colorDivider">
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/imgRestaurantLogo"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:scaleType="centerCrop"
|
|
||||||
android:src="@drawable/circle_bg" />
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnChangeLogo"
|
|
||||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center_horizontal"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="Alterar Logótipo"
|
|
||||||
android:textAllCaps="false" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:text="Configure o raio de segurança para check-in antecipado e regras associadas."
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:strokeWidth="1dp"
|
|
||||||
app:strokeColor="@color/colorDivider">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="24dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Raio de Segurança (metros)"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputRadius"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:hint="Ex: 500"
|
|
||||||
android:inputType="number"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:textColor="@color/colorTextPrimary" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:text="Informação do Restaurante"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputAddress"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:hint="Morada (Ex: Rua Estreita, 12)"
|
|
||||||
android:inputType="textPostalAddress"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:textColor="@color/colorTextPrimary" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:text="Categoria"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="15sp" />
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/spinnerCategory"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:background="@drawable/input_bg" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnSaveSettings"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
app:cornerRadius="14dp"
|
|
||||||
app:backgroundTint="@color/colorPrimary"
|
|
||||||
android:text="Guardar Definições"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,183 +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/detalhesReservasRoot"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/colorBackground"
|
|
||||||
tools:context=".DetalhesReservasActivity">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnVoltar"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="← Voltar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtTituloDetalhes"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Detalhes das reservas"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="26sp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
|
||||||
android:layout_marginTop="20dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtDescricaoDetalhes"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="32dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Selecione uma reserva para ver os detalhes e atualizar o estado."
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtTituloDetalhes" />
|
|
||||||
|
|
||||||
<ListView
|
|
||||||
android:id="@+id/listReservas"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:divider="@color/colorDivider"
|
|
||||||
android:dividerHeight="1dp"
|
|
||||||
android:scrollbars="none"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtDescricaoDetalhes"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/cardDetalhes"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/cardDetalhes"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:cardElevation="10dp"
|
|
||||||
app:strokeWidth="1dp"
|
|
||||||
app:strokeColor="@color/colorDivider"
|
|
||||||
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="Reserva selecionada"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtReservaInfo"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="Selecione uma reserva"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="15sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtReservaNotas"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:text=""
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtReservaEstado"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Estado:"
|
|
||||||
android:textColor="@color/colorSuccess"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnConfirmarReserva"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginEnd="4dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
app:backgroundTint="@color/colorPrimary"
|
|
||||||
android:text="Confirmar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/white" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnCancelarReserva"
|
|
||||||
style="@style/Widget.Material3.Button.TonalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginHorizontal="4dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
android:text="Recusar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorTextPrimary" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnApagarReserva"
|
|
||||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
app:strokeColor="@color/colorError"
|
|
||||||
android:text="Apagar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorError"
|
|
||||||
android:visibility="gone" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtMensagemReserva"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text=""
|
|
||||||
android:textColor="@color/colorSuccess"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,329 +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/establishmentRoot"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/colorBackground"
|
|
||||||
tools:context=".EstablishmentDashboardActivity">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnVoltar"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="← Voltar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:fillViewport="true"
|
|
||||||
android:scrollbars="none"
|
|
||||||
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:id="@+id/txtEstabTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Estabelecimento"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtEstabSubtitle"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Veja as reservas ao vivo e controle a ocupação."
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:lineSpacingExtra="2dp"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtEstabRole"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Função: ADMIN"
|
|
||||||
android:textColor="@color/colorSuccess"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/cardReservasHoje"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:foreground="?attr/selectableItemBackground"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Reservas"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="13sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtReservasHojeDash"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="00"
|
|
||||||
android:textColor="@color/colorPrimary"
|
|
||||||
android:textSize="30sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:layout_marginEnd="6dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Mesas"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="13sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtMesasLivresDash"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="00"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="30sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="6dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="16dp"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="16dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Espera"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="13sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtListaEsperaDash"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="6dp"
|
|
||||||
android:text="00"
|
|
||||||
android:textColor="@color/colorWarning"
|
|
||||||
android:textSize="30sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="40dp"
|
|
||||||
android:text="Próximas reservas"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:cardElevation="4dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/llProximasReservas"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="24dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="A procurar próximas reservas..."
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="15sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="40dp"
|
|
||||||
android:text="Operações rápidas"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnAbrirEspera"
|
|
||||||
style="@style/Widget.Material3.Button.TonalButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
app:cornerRadius="14dp"
|
|
||||||
android:text="Abrir lista de espera"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnGerirMesas"
|
|
||||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
app:cornerRadius="14dp"
|
|
||||||
android:text="Editar mesas"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnDetalhesReservas"
|
|
||||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
app:cornerRadius="14dp"
|
|
||||||
android:text="Detalhes das reservas"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnGestaoStaff"
|
|
||||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
app:cornerRadius="14dp"
|
|
||||||
android:text="Gestão de staff"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnDefinicoes"
|
|
||||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
app:cornerRadius="14dp"
|
|
||||||
android:text="Definições"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textSize="16sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:strokeWidth="1dp"
|
|
||||||
app:strokeColor="@color/colorWarning"
|
|
||||||
app:cardElevation="2dp">
|
|
||||||
|
|
||||||
<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="Alertas operacionais"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="• Mesa 07 aguarda confirmação há 10 min\n• 2 clientes aguardam resposta no chat"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:lineSpacingExtra="4dp"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,172 +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/gerirMesasRoot"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/colorBackground"
|
|
||||||
tools:context=".GerirMesasActivity">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnVoltar"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="← Voltar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtTituloGerirMesas"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Editar mesas"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
|
||||||
android:layout_marginTop="20dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtDescricaoGerirMesas"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="32dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Atualize rapidamente as mesas disponíveis, capacidade e estado."
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtTituloGerirMesas" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/formMesas"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:strokeWidth="1dp"
|
|
||||||
app:strokeColor="@color/colorDivider"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtDescricaoGerirMesas">
|
|
||||||
|
|
||||||
<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="Editar ou adicionar mesa"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputMesaNumero"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="Número da mesa"
|
|
||||||
android:inputType="number"
|
|
||||||
android:paddingHorizontal="16dp" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputMesaCapacidade"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="Capacidade (lugares)"
|
|
||||||
android:inputType="number"
|
|
||||||
android:paddingHorizontal="16dp" />
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/spinnerEstadoMesa"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="50dp"
|
|
||||||
android:layout_marginTop="12dp" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnGuardarMesa"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
app:backgroundTint="@color/colorPrimary"
|
|
||||||
android:text="Guardar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/white" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnRemoverMesa"
|
|
||||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
app:strokeColor="@color/colorError"
|
|
||||||
android:text="Remover"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorError" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtMensagemMesa"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text=""
|
|
||||||
android:textColor="@color/colorSuccess"
|
|
||||||
android:textSize="13sp" />
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtListaTitulo"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:text="Mesas registadas"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/formMesas" />
|
|
||||||
|
|
||||||
<ListView
|
|
||||||
android:id="@+id/listMesas"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:divider="@color/colorDivider"
|
|
||||||
android:dividerHeight="1dp"
|
|
||||||
android:scrollbars="none"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtListaTitulo"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,188 +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/gestaoStaffRoot"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/colorBackground"
|
|
||||||
tools:context=".GestaoStaffActivity">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnVoltar"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="← Voltar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtTituloGestaoStaff"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Gestão de staff"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
android:layout_marginTop="20dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtDescricaoGestaoStaff"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="32dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text="Zona para configurar turnos, equipas e disponibilidade do staff."
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtTituloGestaoStaff"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/formStaff"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:cardElevation="2dp"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:strokeWidth="1dp"
|
|
||||||
app:strokeColor="@color/colorDivider"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtDescricaoGestaoStaff">
|
|
||||||
|
|
||||||
<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="Atribuir funcionário a uma mesa"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/spinnerNomeStaff"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:padding="0dp" />
|
|
||||||
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/spinnerMesaStaff"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:padding="0dp" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnAtribuirStaff"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
app:backgroundTint="@color/colorPrimary"
|
|
||||||
android:text="Guardar atribuição"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/white" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnEliminarStaff"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Eliminar Funcionário"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorError" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtMensagemStaff"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text=""
|
|
||||||
android:textColor="@color/colorSuccess"
|
|
||||||
android:textSize="13sp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtListaStaffTitulo"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:text="Funcionários e mesas"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/formStaff" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnGerirMesasStaff"
|
|
||||||
style="@style/Widget.Material3.Button.OutlinedButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="48dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
app:cornerRadius="12dp"
|
|
||||||
android:text="Gerir Mesas"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
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"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginStart="24dp"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:divider="@color/colorDivider"
|
|
||||||
android:dividerHeight="1dp"
|
|
||||||
android:scrollbars="none"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtListaStaffTitulo"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/floatingActionButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginEnd="24dp"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:clickable="true"
|
|
||||||
app:backgroundTint="@color/colorPrimary"
|
|
||||||
app:tint="@color/white"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
tools:srcCompat="@tools:sample/avatars" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -1,159 +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/listaEsperaRoot"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@color/colorBackground"
|
|
||||||
tools:context=".ListaEsperaActivity">
|
|
||||||
|
|
||||||
<!-- Header Section -->
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnVoltar"
|
|
||||||
style="@style/Widget.Material3.Button.TextButton"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:text="← Voltar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorPrimary"
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtTituloListaEspera"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Lista de espera"
|
|
||||||
android:textSize="28sp"
|
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/btnVoltar"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
android:layout_marginTop="24dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtDescricaoListaEspera"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="32dp"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="Aqui o estabelecimento irá gerir a lista de espera em tempo real."
|
|
||||||
android:textSize="15sp"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:lineSpacingExtra="4dp"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtTituloListaEspera"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<!-- List Section -->
|
|
||||||
<ListView
|
|
||||||
android:id="@+id/listReservasP"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:divider="@color/colorDivider"
|
|
||||||
android:dividerHeight="1dp"
|
|
||||||
android:scrollbars="none"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:paddingBottom="16dp"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtDescricaoListaEspera"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/detalhesCardP" />
|
|
||||||
|
|
||||||
<!-- Selection/Details Card Bottom -->
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/detalhesCardP"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginBottom="32dp"
|
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:cardElevation="12dp"
|
|
||||||
app:strokeWidth="1dp"
|
|
||||||
app:strokeColor="#E2E8F0"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="24dp">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtReservaInfoP"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Selecione uma reserva"
|
|
||||||
android:textColor="@color/colorTextPrimary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:fontFamily="sans-serif-medium" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtReservaNotasP"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text=""
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:lineSpacingExtra="2dp"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtMensagemReservaP"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:text=""
|
|
||||||
android:textColor="@color/colorPrimary"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textStyle="italic" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:weightSum="2">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnRecusarReservaP"
|
|
||||||
style="@style/Widget.Material3.Button.TonalButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:cornerRadius="14dp"
|
|
||||||
app:backgroundTint="#FEE2E2"
|
|
||||||
android:text="Recusar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorError"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/btnConfirmarReservaP"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_weight="1"
|
|
||||||
app:cornerRadius="14dp"
|
|
||||||
app:backgroundTint="@color/colorSuccess"
|
|
||||||
android:text="Aceitar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
@@ -2,229 +2,174 @@
|
|||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/main"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:id="@+id/main"
|
android:background="@color/colorPrimary"
|
||||||
android:background="@color/colorBackground"
|
|
||||||
tools:context=".MainActivity">
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
<ScrollView
|
<ImageView
|
||||||
|
android:id="@+id/imgBg"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:fillViewport="true">
|
android:scaleType="centerCrop"
|
||||||
|
android:alpha="0.3"
|
||||||
|
android:src="@drawable/na_mesa" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<LinearLayout
|
||||||
|
android:id="@+id/header"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="80dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/logoNaMesa"
|
android:layout_width="80dp"
|
||||||
android:layout_width="160dp"
|
android:layout_height="80dp"
|
||||||
android:layout_height="156dp"
|
android:src="@drawable/na_mesa" />
|
||||||
android:layout_marginTop="124dp"
|
|
||||||
android:src="@drawable/na_mesa"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.508"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtTitle"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
android:text="NaMesa"
|
android:text="NaMesa"
|
||||||
android:textColor="@color/colorTextPrimary"
|
android:textColor="@color/white"
|
||||||
android:textSize="28sp"
|
android:textSize="32sp" />
|
||||||
android:fontFamily="sans-serif-medium"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/logoNaMesa" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtSub"
|
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="6dp"
|
android:text="Premium Dining Reservations"
|
||||||
android:text="Sistema de Reserva de Mesas"
|
android:textColor="@color/white"
|
||||||
android:textColor="@color/colorTextSecondary"
|
android:alpha="0.7"
|
||||||
android:textSize="15sp"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtTitle" />
|
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:id="@+id/cardContainer"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginHorizontal="24dp"
|
|
||||||
android:layout_marginTop="32dp"
|
|
||||||
android:layout_marginBottom="40dp"
|
|
||||||
app:cardCornerRadius="24dp"
|
|
||||||
app:cardElevation="8dp"
|
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/txtSub"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.0">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="387dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="24dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/bg_tabs"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:padding="3dp">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnEntrar"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@drawable/tab_selected"
|
|
||||||
android:text="Entrar"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="#231F1F"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnCriarConta"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:text="Criar Conta"
|
|
||||||
android:textAllCaps="false"
|
|
||||||
android:textColor="@color/colorTextSecondary"
|
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<EditText
|
<com.google.android.material.card.MaterialCardView
|
||||||
android:id="@+id/inputName"
|
android:id="@+id/cardContainer"
|
||||||
android:layout_width="match_parent"
|
style="@style/Widget.NaMesa.Card"
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="O seu nome"
|
|
||||||
android:inputType="textPersonName"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputEmail"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="Email"
|
|
||||||
android:inputType="textEmailAddress"
|
|
||||||
android:paddingHorizontal="16dp" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputOwnerPhone"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="Telefone do proprietário"
|
|
||||||
android:inputType="phone"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputEstablishmentName"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="Nome do estabelecimento"
|
|
||||||
android:inputType="textCapWords"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputEstablishmentEmail"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="Email do estabelecimento"
|
|
||||||
android:inputType="textEmailAddress"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<EditText
|
|
||||||
android:id="@+id/inputEstablishmentPhone"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:background="@drawable/input_bg"
|
|
||||||
android:hint="Telefone do estabelecimento"
|
|
||||||
android:inputType="phone"
|
|
||||||
android:paddingHorizontal="16dp"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp">
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginBottom="48dp"
|
||||||
|
app:cardBackgroundColor="@color/white"
|
||||||
|
app:cardElevation="12dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
|
|
||||||
<EditText
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButtonToggleGroup
|
||||||
|
android:id="@+id/toggleGroup"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
app:singleSelection="true"
|
||||||
|
app:checkedButton="@+id/btnEntrar">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnEntrar"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Entrar" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnCriarConta"
|
||||||
|
style="@style/Widget.Material3.Button.OutlinedButton"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Registar" />
|
||||||
|
|
||||||
|
</com.google.android.material.button.MaterialButtonToggleGroup>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/layoutName"
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Nome Completo"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:startIconDrawable="@android:drawable/ic_menu_edit">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/inputName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textPersonName" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:hint="Email"
|
||||||
|
app:startIconDrawable="@android:drawable/ic_dialog_email">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
|
android:id="@+id/inputEmail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textEmailAddress" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:hint="Palavra-passe"
|
||||||
|
app:endIconMode="password_toggle"
|
||||||
|
app:startIconDrawable="@android:drawable/ic_lock_idle_lock">
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputEditText
|
||||||
android:id="@+id/inputPassword"
|
android:id="@+id/inputPassword"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="54dp"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/input_bg"
|
android:inputType="textPassword" />
|
||||||
android:hint="Palavra-passe"
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
android:inputType="textPassword"
|
|
||||||
android:paddingStart="16dp"
|
|
||||||
android:paddingEnd="48dp" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/iconPasswordVisibility"
|
|
||||||
android:layout_width="48dp"
|
|
||||||
android:layout_height="54dp"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
|
||||||
android:clickable="true"
|
|
||||||
android:focusable="true"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:src="@drawable/ic_visibility_off" />
|
|
||||||
</RelativeLayout>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtForgotPassword"
|
android:id="@+id/txtForgotPassword"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="end"
|
android:layout_gravity="end"
|
||||||
android:layout_marginTop="12dp"
|
android:layout_marginBottom="24dp"
|
||||||
android:clickable="true"
|
android:text="Esqueceu-se da senha?"
|
||||||
android:focusable="true"
|
|
||||||
android:text="Esqueceu-se da palavra-passe?"
|
|
||||||
android:textColor="@color/colorPrimary"
|
android:textColor="@color/colorPrimary"
|
||||||
android:textSize="14sp"
|
android:textSize="12sp"
|
||||||
android:visibility="visible" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/btnFinalCriarConta"
|
android:id="@+id/btnPrimaryAction"
|
||||||
|
style="@style/Widget.NaMesa.Button"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="56dp"
|
android:layout_height="60dp"
|
||||||
android:layout_marginTop="24dp"
|
|
||||||
android:text="Entrar"
|
android:text="Entrar"
|
||||||
android:textAllCaps="false"
|
android:textSize="16sp" />
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="16sp"
|
|
||||||
app:backgroundTint="@color/colorPrimary"
|
|
||||||
app:cornerRadius="14dp" />
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</ScrollView>
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBarLogin"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:indeterminateTint="@color/white"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -161,6 +161,24 @@
|
|||||||
android:textColor="@color/colorTextPrimary"
|
android:textColor="@color/colorTextPrimary"
|
||||||
app:strokeColor="@color/colorDivider" />
|
app:strokeColor="@color/colorDivider" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="Escolha a mesa"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvTables"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:spanCount="3"
|
||||||
|
tools:listitem="@layout/item_table" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -180,6 +198,28 @@
|
|||||||
android:hint="Ex: 2"
|
android:hint="Ex: 2"
|
||||||
android:textColorHint="@color/colorTextHint"
|
android:textColorHint="@color/colorTextHint"
|
||||||
android:textColor="@color/colorTextPrimary" />
|
android:textColor="@color/colorTextPrimary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:text="Notas adicionais (opcional)"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etNotes"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:background="@drawable/bg_input_modern"
|
||||||
|
android:inputType="textMultiLine"
|
||||||
|
android:minHeight="100dp"
|
||||||
|
android:gravity="top"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:hint="Alguma preferência?"
|
||||||
|
android:textColorHint="@color/colorTextHint"
|
||||||
|
android:textColor="@color/colorTextPrimary" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|||||||
46
app/src/main/res/layout/activity_onboarding.xml
Normal file
46
app/src/main/res/layout/activity_onboarding.xml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/logo"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:src="@drawable/na_mesa"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/tagline"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tagline"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:text="A sua mesa, à sua espera."
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/btnGetStarted"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/logo" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnGetStarted"
|
||||||
|
style="@style/Widget.NaMesa.Button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginHorizontal="32dp"
|
||||||
|
android:layout_marginBottom="48dp"
|
||||||
|
android:text="Começar"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
203
app/src/main/res/layout/activity_reservation.xml
Normal file
203
app/src/main/res/layout/activity_reservation.xml
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
<?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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="@color/white"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:navigationIcon="@android:drawable/ic_menu_revert"
|
||||||
|
app:title="Reservar Mesa"
|
||||||
|
app:titleTextColor="@color/colorTextPrimary" />
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:fillViewport="true"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/confirmContainer"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/toolbar">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<!-- Number of People -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Número de Pessoas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="@style/Widget.NaMesa.Card"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
app:strokeColor="@color/colorBorder"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnDecreaseGuests"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@android:drawable/ic_menu_delete"
|
||||||
|
app:tint="@color/colorPrimary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtGuestCount"
|
||||||
|
android:layout_width="80dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="2"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/btnIncreaseGuests"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
|
android:src="@android:drawable/ic_menu_add"
|
||||||
|
app:tint="@color/colorPrimary" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Date Selection -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Data da Reserva"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardDatePicker"
|
||||||
|
style="@style/Widget.NaMesa.Card"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
app:strokeColor="@color/colorBorder"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@android:drawable/ic_menu_my_calendar"
|
||||||
|
app:tint="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtSelectedDate"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Selecionar Data"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Time Slots -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Horários Disponíveis"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvTimeSlots"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
app:spanCount="3"
|
||||||
|
tools:listitem="@layout/item_time_slot" />
|
||||||
|
|
||||||
|
<!-- Special Requests -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Pedidos Especiais (Opcional)"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="@style/Widget.NaMesa.Card"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
app:strokeColor="@color/colorBorder"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSpecialRequests"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@null"
|
||||||
|
android:gravity="top"
|
||||||
|
android:hint="Ex: Cadeira para bebé, alergias..."
|
||||||
|
android:padding="16dp"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textColorHint="@color/colorTextHint"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/confirmContainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:cardBackgroundColor="@color/white"
|
||||||
|
app:cardCornerRadius="0dp"
|
||||||
|
app:cardElevation="16dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:strokeWidth="0dp">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnConfirmReservation"
|
||||||
|
style="@style/Widget.NaMesa.Button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:text="Confirmar Reserva"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
163
app/src/main/res/layout/activity_restaurant_detail.xml
Normal file
163
app/src/main/res/layout/activity_restaurant_detail.xml
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appBar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="300dp"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
|
android:id="@+id/collapsingToolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:contentScrim="@color/colorPrimary"
|
||||||
|
app:layout_scrollFlags="scroll|exitUntilCollapsed"
|
||||||
|
app:titleEnabled="false">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/restaurantCover"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/na_mesa"
|
||||||
|
app:layout_collapseMode="parallax" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/gradient_overlay" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:layout_collapseMode="pin"
|
||||||
|
app:navigationIcon="@android:drawable/ic_menu_revert" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.NestedScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detailName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Nome do Restaurante"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detailCuisine"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Italiano"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="8dp"
|
||||||
|
android:text="•"
|
||||||
|
android:textColor="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detailRating"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="4.8 ★"
|
||||||
|
android:textColor="@color/colorWarning"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Sobre"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detailDescription"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:text="Descrição do restaurante aparecerá aqui com todos os detalhes premium."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Localização"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/detailAddress"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Rua Principal, 123, Lisboa"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
|
||||||
|
<!-- Spacer for bottom button -->
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="100dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
app:cardBackgroundColor="@color/white"
|
||||||
|
app:cardCornerRadius="0dp"
|
||||||
|
app:cardElevation="16dp"
|
||||||
|
app:strokeWidth="0dp">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnReserveNow"
|
||||||
|
style="@style/Widget.NaMesa.Button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:text="Reservar Mesa"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
47
app/src/main/res/layout/activity_splash.xml
Normal file
47
app/src/main/res/layout/activity_splash.xml
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/colorPrimary">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/splash_logo"
|
||||||
|
android:layout_width="150dp"
|
||||||
|
android:layout_height="150dp"
|
||||||
|
android:src="@drawable/na_mesa"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/splash_title"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/splash_title"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="NaMesa"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="40sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@+id/splash_subtitle"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/splash_logo" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/splash_subtitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:alpha="0.8"
|
||||||
|
android:text="Sempre um lugar para si"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/splash_title" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
174
app/src/main/res/layout/fragment_home.xml
Normal file
174
app/src/main/res/layout/fragment_home.xml
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.core.widget.NestedScrollView 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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/white"
|
||||||
|
android:fillViewport="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="80dp">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
android:paddingTop="32dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/greeting"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Olá, Explorador"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="32sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Onde gostaria de jantar hoje?"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<!-- Search Bar -->
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="@style/Widget.NaMesa.Card"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="56dp"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
app:strokeColor="@color/colorBorder"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:src="@android:drawable/ic_menu_search"
|
||||||
|
app:tint="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearch"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:background="@null"
|
||||||
|
android:hint="Procurar restaurantes, cozinhas..."
|
||||||
|
android:inputType="text"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textColorHint="@color/colorTextHint"
|
||||||
|
android:textSize="15sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<!-- Categories -->
|
||||||
|
<HorizontalScrollView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
android:scrollbars="none">
|
||||||
|
|
||||||
|
<com.google.android.material.chip.ChipGroup
|
||||||
|
android:id="@+id/categoryChips"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:singleLine="true"
|
||||||
|
app:singleSelection="true">
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
style="@style/Widget.Material3.Chip.Filter"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Tudo"
|
||||||
|
app:chipBackgroundColor="@color/colorChip"
|
||||||
|
app:chipStrokeWidth="0dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
style="@style/Widget.Material3.Chip.Filter"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Italiano"
|
||||||
|
app:chipBackgroundColor="@color/colorChip"
|
||||||
|
app:chipStrokeWidth="0dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
style="@style/Widget.Material3.Chip.Filter"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Sushi"
|
||||||
|
app:chipBackgroundColor="@color/colorChip"
|
||||||
|
app:chipStrokeWidth="0dp" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
style="@style/Widget.Material3.Chip.Filter"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Hambúrgueres"
|
||||||
|
app:chipBackgroundColor="@color/colorChip"
|
||||||
|
app:chipStrokeWidth="0dp" />
|
||||||
|
</com.google.android.material.chip.ChipGroup>
|
||||||
|
</HorizontalScrollView>
|
||||||
|
|
||||||
|
<!-- Featured Section -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Destaques"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="22sp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvFeatured"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant_featured" />
|
||||||
|
|
||||||
|
<!-- Near You Section -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Perto de si"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="22sp" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvNearYou"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
tools:listitem="@layout/item_restaurant_card" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
71
app/src/main/res/layout/fragment_my_reservations.xml
Normal file
71
app/src/main/res/layout/fragment_my_reservations.xml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?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:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTitle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="24dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Minhas Reservas"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="28sp"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rvReservations"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="80dp"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/txtTitle"
|
||||||
|
tools:listitem="@layout/item_reserva_cliente" />
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/progressBar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/emptyState"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:alpha="0.5"
|
||||||
|
android:src="@drawable/na_mesa" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="Ainda não tem reservas."
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
121
app/src/main/res/layout/fragment_profile.xml
Normal file
121
app/src/main/res/layout/fragment_profile.xml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/white">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/headerBg"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:background="@color/colorPrimary"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/cardProfile"
|
||||||
|
android:layout_width="120dp"
|
||||||
|
android:layout_height="120dp"
|
||||||
|
android:layout_marginTop="140dp"
|
||||||
|
app:cardCornerRadius="60dp"
|
||||||
|
app:cardElevation="8dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:strokeColor="@color/white"
|
||||||
|
app:strokeWidth="4dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imgProfile"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/na_mesa" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtProfileName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Nome do Utilizador"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="24sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/cardProfile" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtProfileEmail"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="email@exemplo.com"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/txtProfileName" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="40dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/txtProfileEmail">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnEditProfile"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:gravity="start|center_vertical"
|
||||||
|
android:text="Editar Perfil"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:icon="@android:drawable/ic_menu_edit"
|
||||||
|
app:iconPadding="16dp"
|
||||||
|
app:iconTint="@color/colorPrimary" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/colorBorder" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnNotifications"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:gravity="start|center_vertical"
|
||||||
|
android:text="Notificações"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:icon="@android:drawable/ic_popup_reminder"
|
||||||
|
app:iconPadding="16dp"
|
||||||
|
app:iconTint="@color/colorPrimary" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:background="@color/colorBorder" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnLogout"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="60dp"
|
||||||
|
android:layout_marginTop="32dp"
|
||||||
|
android:gravity="start|center_vertical"
|
||||||
|
android:text="Sair da Conta"
|
||||||
|
android:textColor="@color/colorError"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:icon="@android:drawable/ic_lock_power_off"
|
||||||
|
app:iconPadding="16dp"
|
||||||
|
app:iconTint="@color/colorError" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/Widget.NaMesa.Card"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="20dp"
|
android:layout_marginHorizontal="24dp"
|
||||||
android:layout_marginVertical="8dp"
|
android:layout_marginVertical="8dp"
|
||||||
app:cardCornerRadius="20dp"
|
|
||||||
app:cardElevation="2dp"
|
app:cardElevation="2dp"
|
||||||
app:cardBackgroundColor="@color/colorSurface"
|
|
||||||
app:strokeWidth="0dp">
|
app:strokeWidth="0dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
96
app/src/main/res/layout/item_reserva_local.xml
Normal file
96
app/src/main/res/layout/item_reserva_local.xml
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="8dp"
|
||||||
|
app:cardCornerRadius="16dp"
|
||||||
|
app:cardElevation="4dp"
|
||||||
|
app:strokeWidth="0dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtRestauranteNome"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="Nome do Restaurante"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<com.google.android.material.chip.Chip
|
||||||
|
android:id="@+id/chipStatus"
|
||||||
|
style="@style/Widget.Material3.Chip.Suggestion"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="PENDENTE"
|
||||||
|
app:chipBackgroundColor="@color/colorChipPending"
|
||||||
|
android:textColor="@color/colorChipPendingText" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtDataHora"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="15/05/2026 às 20:00"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtMesaPessoas"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Mesa 5 • 4 pessoas"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
android:background="@color/colorDivider" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnCancelar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Cancelar"
|
||||||
|
android:textColor="@color/colorError" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/btnEditar"
|
||||||
|
style="@style/Widget.Material3.Button.TextButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Ver Detalhes"
|
||||||
|
android:textColor="@color/colorPrimary" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
67
app/src/main/res/layout/item_restaurant_card.xml
Normal file
67
app/src/main/res/layout/item_restaurant_card.xml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/Widget.NaMesa.Card"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:strokeWidth="0dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="12dp">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:layout_width="100dp"
|
||||||
|
android:layout_height="100dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
app:strokeWidth="0dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/restaurantImage"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:src="@drawable/na_mesa" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/restaurantName"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif-black"
|
||||||
|
android:text="Nome do Restaurante"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/restaurantCuisine"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Cozinha • 4.9 ★"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="14sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="Ver detalhes"
|
||||||
|
android:textColor="@color/colorPrimary"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/Widget.NaMesa.Card"
|
||||||
android:layout_width="280dp"
|
android:layout_width="280dp"
|
||||||
android:layout_height="180dp"
|
android:layout_height="200dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginVertical="8dp"
|
android:layout_marginBottom="8dp"
|
||||||
app:cardCornerRadius="24dp"
|
|
||||||
app:cardElevation="4dp"
|
app:cardElevation="4dp"
|
||||||
app:strokeWidth="0dp">
|
app:strokeWidth="0dp">
|
||||||
|
|
||||||
@@ -14,112 +14,40 @@
|
|||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/imgFeaturedCover"
|
android:id="@+id/restaurantImage"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="match_parent"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:src="@mipmap/ic_launcher"
|
android:src="@drawable/na_mesa" />
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="80dp"
|
android:layout_height="match_parent"
|
||||||
android:background="@drawable/gradient_shadow_up"
|
android:background="@drawable/gradient_overlay" />
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/txtFeaturedName"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:text="Restaurante"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:textSize="20sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:fontFamily="sans-serif"
|
|
||||||
app:layout_constraintBottom_toTopOf="@id/layoutRating"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent" />
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/btnFavorite"
|
|
||||||
android:layout_width="40dp"
|
|
||||||
android:layout_height="40dp"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginEnd="12dp"
|
|
||||||
android:background="@drawable/bg_circle_white"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:scaleType="fitCenter"
|
|
||||||
android:src="@android:drawable/btn_star_big_off"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:tint="@color/colorTextPrimary" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layoutRating"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="vertical"
|
||||||
android:layout_marginStart="16dp"
|
android:padding="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
app:layout_constraintBottom_toBottomOf="parent">
|
||||||
android:gravity="center_vertical"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent">
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:layout_width="14dp"
|
|
||||||
android:layout_height="14dp"
|
|
||||||
android:src="@android:drawable/star_on"
|
|
||||||
app:tint="@color/colorWarning" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtFeaturedRating"
|
android:id="@+id/restaurantName"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="4dp"
|
android:fontFamily="sans-serif-black"
|
||||||
android:text="4.8"
|
android:text="Nome do Restaurante"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="14sp"
|
android:textSize="18sp" />
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/txtFeaturedCategory"
|
android:id="@+id/restaurantCuisine"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="6dp"
|
android:text="Cozinha • 4.9 ★"
|
||||||
android:text="• Sushi"
|
android:textColor="@color/white"
|
||||||
android:textColor="#E0E0E0"
|
|
||||||
android:textSize="14sp" />
|
android:textSize="14sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.card.MaterialCardView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="12dp"
|
|
||||||
android:layout_marginStart="12dp"
|
|
||||||
app:cardCornerRadius="12dp"
|
|
||||||
app:cardBackgroundColor="#CCFFFFFF"
|
|
||||||
app:strokeWidth="0dp"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingHorizontal="12dp"
|
|
||||||
android:paddingVertical="6dp"
|
|
||||||
android:text="Destaque"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:textColor="@color/colorTextPrimary" />
|
|
||||||
</com.google.android.material.card.MaterialCardView>
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</com.google.android.material.card.MaterialCardView>
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|||||||
48
app/src/main/res/layout/item_table.xml
Normal file
48
app/src/main/res/layout/item_table.xml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/cardTable"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
app:cardCornerRadius="12dp"
|
||||||
|
app:cardElevation="2dp"
|
||||||
|
app:strokeWidth="2dp"
|
||||||
|
app:strokeColor="@color/colorBorder"
|
||||||
|
app:cardBackgroundColor="@color/white">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="12dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imgTableStatus"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:src="@android:drawable/ic_menu_agenda"
|
||||||
|
app:tint="@color/colorTextSecondary" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTableNumber"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:text="Mesa 1"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="14sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTableStatus"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Livre"
|
||||||
|
android:textColor="@color/colorTextSecondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
22
app/src/main/res/layout/item_time_slot.xml
Normal file
22
app/src/main/res/layout/item_time_slot.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/cardTimeSlot"
|
||||||
|
style="@style/Widget.NaMesa.Card"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="50dp"
|
||||||
|
android:layout_margin="4dp"
|
||||||
|
app:cardElevation="0dp"
|
||||||
|
app:strokeColor="@color/colorBorder"
|
||||||
|
app:strokeWidth="1dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtTimeSlot"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="19:30"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
android:textSize="15sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
11
app/src/main/res/layout/layout_filter_chip.xml
Normal file
11
app/src/main/res/layout/layout_filter_chip.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
style="@style/Widget.Material3.Chip.Filter"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:checkable="true"
|
||||||
|
android:textColor="@color/colorTextPrimary"
|
||||||
|
app:chipBackgroundColor="@color/colorChip"
|
||||||
|
app:chipStrokeWidth="0dp"
|
||||||
|
app:rippleColor="@color/colorPrimary" />
|
||||||
@@ -1,31 +1,33 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<!-- Brand Colors - Warm Premium Palette -->
|
<!-- Brand Colors - Midnight Blue Premium Palette -->
|
||||||
<color name="colorPrimary">#FF5A5F</color> <!-- Airbnb/UberEats Coral -->
|
<color name="colorPrimary">#1A237E</color>
|
||||||
<color name="colorPrimaryVariant">#FF3B30</color>
|
<color name="colorPrimaryVariant">#000051</color>
|
||||||
<color name="colorSecondary">#FF5A5F</color>
|
<color name="colorSecondary">#3F51B5</color>
|
||||||
|
|
||||||
<!-- Neutral Colors -->
|
<!-- Neutral Colors -->
|
||||||
<color name="black">#FF000000</color>
|
<color name="black">#121212</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
<color name="white">#FFFFFF</color>
|
||||||
<color name="colorBackground">#F7F7F9</color> <!-- Soft grey for modern background -->
|
<color name="colorBackground">#FFFFFF</color>
|
||||||
<color name="colorSurface">#FFFFFF</color>
|
<color name="colorSurface">#FFFFFF</color>
|
||||||
<color name="colorTextPrimary">#1A1A1A</color> <!-- Dark but not pure black -->
|
<color name="colorTextPrimary">#121212</color>
|
||||||
<color name="colorTextSecondary">#757575</color>
|
<color name="colorTextSecondary">#666666</color>
|
||||||
<color name="colorTextHint">#BDBDBD</color>
|
<color name="colorTextHint">#A0A0A0</color>
|
||||||
|
<color name="colorBorder">#EEEEEE</color>
|
||||||
|
|
||||||
<!-- Semantic Colors -->
|
<!-- Semantic Colors -->
|
||||||
<color name="colorSuccess">#34C759</color> <!-- iOS green -->
|
<color name="colorSuccess">#00897B</color>
|
||||||
<color name="colorError">#FF3B30</color>
|
<color name="colorError">#D32F2F</color>
|
||||||
<color name="colorWarning">#FFCC00</color>
|
<color name="colorWarning">#FFA000</color>
|
||||||
<color name="colorDivider">#E0E0E0</color>
|
<color name="colorDivider">#F5F5F5</color>
|
||||||
<color name="colorChipPending">#FFE0B2</color>
|
<color name="colorChip">#F8F9FA</color>
|
||||||
<color name="colorChipPendingText">#E65100</color>
|
<color name="colorAccent">#1A237E</color>
|
||||||
<color name="colorChipConfirmed">#C8E6C9</color>
|
|
||||||
<color name="colorChipConfirmedText">#1B5E20</color>
|
|
||||||
<color name="colorChipCancelled">#FFCDD2</color>
|
|
||||||
<color name="colorChipCancelledText">#B71C1C</color>
|
|
||||||
|
|
||||||
<!-- Legacy compatibility -->
|
<!-- Status Colors (Restored for compatibility and new design) -->
|
||||||
<color name="my_light_primary">#BE1F13</color>
|
<color name="colorChipPending">#FFF3E0</color>
|
||||||
|
<color name="colorChipPendingText">#E65100</color>
|
||||||
|
<color name="colorChipConfirmed">#E8F5E9</color>
|
||||||
|
<color name="colorChipConfirmedText">#2E7D32</color>
|
||||||
|
<color name="colorChipCancelled">#FFEBEE</color>
|
||||||
|
<color name="colorChipCancelledText">#C62828</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -1,20 +1,45 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<!-- Base application theme. -->
|
<!-- Base application theme. -->
|
||||||
<style name="Base.Theme.Pap_teste" parent="Theme.Material3.DayNight.NoActionBar">
|
<style name="Base.Theme.Pap_teste" parent="Theme.Material3.Light.NoActionBar">
|
||||||
<!-- Core Colors -->
|
<!-- Core Colors -->
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
|
<item name="colorPrimaryVariant">@color/colorPrimaryVariant</item>
|
||||||
<item name="colorSecondary">@color/colorSecondary</item>
|
<item name="colorSecondary">@color/colorSecondary</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
|
||||||
<!-- Background and Surface Colors -->
|
<!-- Background and Surface Colors -->
|
||||||
<item name="android:colorBackground">@color/colorBackground</item>
|
<item name="android:colorBackground">@color/colorBackground</item>
|
||||||
<item name="colorSurface">@color/colorSurface</item>
|
<item name="colorSurface">@color/colorSurface</item>
|
||||||
<item name="colorError">@color/colorError</item>
|
<item name="colorError">@color/colorError</item>
|
||||||
|
|
||||||
<!-- Typography base generic configuration -->
|
<!-- Status Bar -->
|
||||||
<item name="android:fontFamily">sans-serif</item>
|
<item name="android:statusBarColor">@color/white</item>
|
||||||
|
<item name="android:windowLightStatusBar">true</item>
|
||||||
|
|
||||||
|
<!-- Typography -->
|
||||||
|
<item name="android:fontFamily">sans-serif-medium</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Theme.Pap_teste" parent="Base.Theme.Pap_teste" />
|
<style name="Theme.Pap_teste" parent="Base.Theme.Pap_teste" />
|
||||||
|
|
||||||
|
<!-- Premium Button Style -->
|
||||||
|
<style name="Widget.NaMesa.Button" parent="Widget.Material3.Button">
|
||||||
|
<item name="shapeAppearanceOverlay">@style/ShapeAppearance.NaMesa.Rounded</item>
|
||||||
|
<item name="android:insetTop">0dp</item>
|
||||||
|
<item name="android:insetBottom">0dp</item>
|
||||||
|
<item name="android:textAllCaps">false</item>
|
||||||
|
<item name="android:letterSpacing">0.02</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Premium Card Style -->
|
||||||
|
<style name="Widget.NaMesa.Card" parent="Widget.Material3.CardView.Elevated">
|
||||||
|
<item name="cardCornerRadius">16dp</item>
|
||||||
|
<item name="cardElevation">2dp</item>
|
||||||
|
<item name="cardBackgroundColor">@color/white</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="ShapeAppearance.NaMesa.Rounded" parent="">
|
||||||
|
<item name="cornerSize">16dp</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -13,6 +13,7 @@ googleServices = "4.4.2"
|
|||||||
firebaseDatabase = "22.0.1"
|
firebaseDatabase = "22.0.1"
|
||||||
glide = "4.16.0"
|
glide = "4.16.0"
|
||||||
firebaseStorage = "21.0.1"
|
firebaseStorage = "21.0.1"
|
||||||
|
room = "2.6.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||||
@@ -27,6 +28,9 @@ firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.r
|
|||||||
firebase-database = { group = "com.google.firebase", name = "firebase-database", version.ref = "firebaseDatabase" }
|
firebase-database = { group = "com.google.firebase", name = "firebase-database", version.ref = "firebaseDatabase" }
|
||||||
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
|
glide = { group = "com.github.bumptech.glide", name = "glide", version.ref = "glide" }
|
||||||
firebase-storage = { group = "com.google.firebase", name = "firebase-storage", version.ref = "firebaseStorage" }
|
firebase-storage = { group = "com.google.firebase", name = "firebase-storage", version.ref = "firebaseStorage" }
|
||||||
|
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
|
||||||
|
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
||||||
|
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Reference in New Issue
Block a user